diff options
Diffstat (limited to 'drivers')
46 files changed, 3413 insertions, 394 deletions
| diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c index 0c1cd831e..510cb28ad 100644 --- a/drivers/dma/apbh_dma.c +++ b/drivers/dma/apbh_dma.c @@ -31,7 +31,8 @@  #include <asm/arch/clock.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/sys_proto.h> -#include <asm/arch/dma.h> +#include <asm/imx-common/dma.h> +#include <asm/imx-common/regs-apbh.h>  static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; @@ -226,7 +227,7 @@ static int mxs_dma_reset(int channel)  #if defined(CONFIG_MX23)  	uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set);  	uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET; -#elif defined(CONFIG_MX28) +#elif (defined(CONFIG_MX28) || defined(CONFIG_MX6))  	uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set);  	uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET;  #endif diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index b48f623c1..0b51dcdef 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -30,6 +30,7 @@ COBJS-y += fpga.o  COBJS-$(CONFIG_FPGA_SPARTAN2) += spartan2.o  COBJS-$(CONFIG_FPGA_SPARTAN3) += spartan3.o  COBJS-$(CONFIG_FPGA_VIRTEX2) += virtex2.o +COBJS-$(CONFIG_FPGA_ZYNQPL) += zynqpl.o  COBJS-$(CONFIG_FPGA_XILINX) += xilinx.o  COBJS-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o  ifdef CONFIG_FPGA_ALTERA diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index 26d244354..f70bff6ed 100644 --- a/drivers/fpga/fpga.c +++ b/drivers/fpga/fpga.c @@ -22,122 +22,99 @@   *   */ -/* - *  Generic FPGA support - */ +/* Generic FPGA support */  #include <common.h>             /* core U-Boot definitions */  #include <xilinx.h>             /* xilinx specific definitions */  #include <altera.h>             /* altera specific definitions */  #include <lattice.h> -#if 0 -#define FPGA_DEBUG              /* define FPGA_DEBUG to get debug messages */ -#endif -  /* Local definitions */  #ifndef CONFIG_MAX_FPGA_DEVICES  #define CONFIG_MAX_FPGA_DEVICES		5  #endif -/* Enable/Disable debug console messages */ -#ifdef FPGA_DEBUG -#define	PRINTF(fmt,args...)	printf (fmt ,##args) -#else -#define	PRINTF(fmt,args...) -#endif -  /* Local static data */  static int next_desc = FPGA_INVALID_DEVICE;  static fpga_desc desc_table[CONFIG_MAX_FPGA_DEVICES]; -/* Local static functions */ -static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_get_desc( int devnum ); -static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_validate(int devnum, const void *buf, -					 size_t bsize, char *fn ); -static int fpga_dev_info( int devnum ); - - -/* ------------------------------------------------------------------------- */ - -/* fpga_no_sup +/* + * fpga_no_sup   * 'no support' message function   */ -static void fpga_no_sup( char *fn, char *msg ) +static void fpga_no_sup(char *fn, char *msg)  { -	if ( fn && msg ) { -		printf( "%s: No support for %s.\n", fn, msg); -	} else if ( msg ) { -		printf( "No support for %s.\n", msg); -	} else { -		printf( "No FPGA suport!\n"); -	} +	if (fn && msg) +		printf("%s: No support for %s.\n", fn, msg); +	else if (msg) +		printf("No support for %s.\n", msg); +	else +		printf("No FPGA suport!\n");  }  /* fpga_get_desc   *	map a device number to a descriptor   */ -static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_get_desc( int devnum ) +static const fpga_desc *const fpga_get_desc(int devnum)  { -	fpga_desc *desc = (fpga_desc * )NULL; +	fpga_desc *desc = (fpga_desc *)NULL; -	if (( devnum >= 0 ) && (devnum < next_desc )) { +	if ((devnum >= 0) && (devnum < next_desc)) {  		desc = &desc_table[devnum]; -		PRINTF( "%s: found fpga descriptor #%d @ 0x%p\n", -				__FUNCTION__, devnum, desc ); +		debug("%s: found fpga descriptor #%d @ 0x%p\n", +		      __func__, devnum, desc);  	}  	return desc;  } - -/* fpga_validate +/* + * fpga_validate   *	generic parameter checking code   */ -static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_validate(int devnum, const void *buf, -					 size_t bsize, char *fn ) +const fpga_desc *const fpga_validate(int devnum, const void *buf, +				     size_t bsize, char *fn)  { -	fpga_desc * desc = fpga_get_desc( devnum ); +	const fpga_desc *desc = fpga_get_desc(devnum); -	if ( !desc ) { -		printf( "%s: Invalid device number %d\n", fn, devnum ); -	} +	if (!desc) +		printf("%s: Invalid device number %d\n", fn, devnum); -	if ( !buf ) { -		printf( "%s: Null buffer.\n", fn ); +	if (!buf) { +		printf("%s: Null buffer.\n", fn);  		return (fpga_desc * const)NULL;  	}  	return desc;  } - -/* fpga_dev_info +/* + * fpga_dev_info   *	generic multiplexing code   */ -static int fpga_dev_info( int devnum ) +static int fpga_dev_info(int devnum)  { -	int ret_val = FPGA_FAIL;           /* assume failure */ -	const fpga_desc * const desc = fpga_get_desc( devnum ); +	int ret_val = FPGA_FAIL; /* assume failure */ +	const fpga_desc * const desc = fpga_get_desc(devnum); -	if ( desc ) { -		PRINTF( "%s: Device Descriptor @ 0x%p\n", -				__FUNCTION__, desc->devdesc ); +	if (desc) { +		debug("%s: Device Descriptor @ 0x%p\n", +		      __func__, desc->devdesc); -		switch ( desc->devtype ) { +		switch (desc->devtype) {  		case fpga_xilinx:  #if defined(CONFIG_FPGA_XILINX) -			printf( "Xilinx Device\nDescriptor @ 0x%p\n", desc ); -			ret_val = xilinx_info( desc->devdesc ); +			printf("Xilinx Device\nDescriptor @ 0x%p\n", desc); +			ret_val = xilinx_info(desc->devdesc);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" ); +			fpga_no_sup((char *)__func__, "Xilinx devices");  #endif  			break;  		case fpga_altera:  #if defined(CONFIG_FPGA_ALTERA) -			printf( "Altera Device\nDescriptor @ 0x%p\n", desc ); -			ret_val = altera_info( desc->devdesc ); +			printf("Altera Device\nDescriptor @ 0x%p\n", desc); +			ret_val = altera_info(desc->devdesc);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Altera devices" ); +			fpga_no_sup((char *)__func__, "Altera devices");  #endif  			break;  		case fpga_lattice: @@ -145,171 +122,183 @@ static int fpga_dev_info( int devnum )  			printf("Lattice Device\nDescriptor @ 0x%p\n", desc);  			ret_val = lattice_info(desc->devdesc);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" ); +			fpga_no_sup((char *)__func__, "Lattice devices");  #endif  			break;  		default: -			printf( "%s: Invalid or unsupported device type %d\n", -					__FUNCTION__, desc->devtype ); +			printf("%s: Invalid or unsupported device type %d\n", +			       __func__, desc->devtype);  		}  	} else { -		printf( "%s: Invalid device number %d\n", -			__FUNCTION__, devnum ); +		printf("%s: Invalid device number %d\n", __func__, devnum);  	}  	return ret_val;  } - -/* ------------------------------------------------------------------------- */ -/* fgpa_init is usually called from misc_init_r() and MUST be called +/* + * fgpa_init is usually called from misc_init_r() and MUST be called   * before any of the other fpga functions are used.   */  void fpga_init(void)  {  	next_desc = 0; -	memset( desc_table, 0, sizeof(desc_table)); +	memset(desc_table, 0, sizeof(desc_table)); -	PRINTF( "%s: CONFIG_FPGA = 0x%x\n", __FUNCTION__, CONFIG_FPGA ); +	debug("%s\n", __func__);  } -/* fpga_count +/* + * fpga_count   * Basic interface function to get the current number of devices available.   */ -int fpga_count( void ) +int fpga_count(void)  {  	return next_desc;  } -/* fpga_add +/* + * fpga_add   *	Add the device descriptor to the device table.   */ -int fpga_add( fpga_type devtype, void *desc ) +int fpga_add(fpga_type devtype, void *desc)  {  	int devnum = FPGA_INVALID_DEVICE; -	if ( next_desc  < 0 ) { -		printf( "%s: FPGA support not initialized!\n", __FUNCTION__ ); -	} else if (( devtype > fpga_min_type ) && ( devtype < fpga_undefined )) { -		if ( desc ) { -			if ( next_desc < CONFIG_MAX_FPGA_DEVICES ) { +	if (next_desc < 0) { +		printf("%s: FPGA support not initialized!\n", __func__); +	} else if ((devtype > fpga_min_type) && (devtype < fpga_undefined)) { +		if (desc) { +			if (next_desc < CONFIG_MAX_FPGA_DEVICES) {  				devnum = next_desc;  				desc_table[next_desc].devtype = devtype;  				desc_table[next_desc++].devdesc = desc;  			} else { -				printf( "%s: Exceeded Max FPGA device count\n", __FUNCTION__ ); +				printf("%s: Exceeded Max FPGA device count\n", +				       __func__);  			}  		} else { -			printf( "%s: NULL device descriptor\n", __FUNCTION__ ); +			printf("%s: NULL device descriptor\n", __func__);  		}  	} else { -		printf( "%s: Unsupported FPGA type %d\n", __FUNCTION__, devtype ); +		printf("%s: Unsupported FPGA type %d\n", __func__, devtype);  	}  	return devnum;  }  /* - *	Generic multiplexing code + * Convert bitstream data and load into the fpga + */ +int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +{ +	printf("Bitstream support not implemented for this FPGA device\n"); +	return FPGA_FAIL; +} + +/* + * Generic multiplexing code   */  int fpga_load(int devnum, const void *buf, size_t bsize)  {  	int ret_val = FPGA_FAIL;           /* assume failure */ -	fpga_desc * desc = fpga_validate( devnum, buf, bsize, (char *)__FUNCTION__ ); +	const fpga_desc *desc = fpga_validate(devnum, buf, bsize, +					      (char *)__func__); -	if ( desc ) { -		switch ( desc->devtype ) { +	if (desc) { +		switch (desc->devtype) {  		case fpga_xilinx:  #if defined(CONFIG_FPGA_XILINX) -			ret_val = xilinx_load( desc->devdesc, buf, bsize ); +			ret_val = xilinx_load(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" ); +			fpga_no_sup((char *)__func__, "Xilinx devices");  #endif  			break;  		case fpga_altera:  #if defined(CONFIG_FPGA_ALTERA) -			ret_val = altera_load( desc->devdesc, buf, bsize ); +			ret_val = altera_load(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Altera devices" ); +			fpga_no_sup((char *)__func__, "Altera devices");  #endif  			break;  		case fpga_lattice:  #if defined(CONFIG_FPGA_LATTICE)  			ret_val = lattice_load(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" ); +			fpga_no_sup((char *)__func__, "Lattice devices");  #endif  			break;  		default: -			printf( "%s: Invalid or unsupported device type %d\n", -				__FUNCTION__, desc->devtype ); +			printf("%s: Invalid or unsupported device type %d\n", +			       __func__, desc->devtype);  		}  	}  	return ret_val;  } -/* fpga_dump +/* + * fpga_dump   *	generic multiplexing code   */  int fpga_dump(int devnum, const void *buf, size_t bsize)  {  	int ret_val = FPGA_FAIL;           /* assume failure */ -	fpga_desc * desc = fpga_validate( devnum, buf, bsize, (char *)__FUNCTION__ ); +	const fpga_desc *desc = fpga_validate(devnum, buf, bsize, +					      (char *)__func__); -	if ( desc ) { -		switch ( desc->devtype ) { +	if (desc) { +		switch (desc->devtype) {  		case fpga_xilinx:  #if defined(CONFIG_FPGA_XILINX) -			ret_val = xilinx_dump( desc->devdesc, buf, bsize ); +			ret_val = xilinx_dump(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" ); +			fpga_no_sup((char *)__func__, "Xilinx devices");  #endif  			break;  		case fpga_altera:  #if defined(CONFIG_FPGA_ALTERA) -			ret_val = altera_dump( desc->devdesc, buf, bsize ); +			ret_val = altera_dump(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Altera devices" ); +			fpga_no_sup((char *)__func__, "Altera devices");  #endif  			break;  		case fpga_lattice:  #if defined(CONFIG_FPGA_LATTICE)  			ret_val = lattice_dump(desc->devdesc, buf, bsize);  #else -			fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" ); +			fpga_no_sup((char *)__func__, "Lattice devices");  #endif  			break;  		default: -			printf( "%s: Invalid or unsupported device type %d\n", -				__FUNCTION__, desc->devtype ); +			printf("%s: Invalid or unsupported device type %d\n", +			       __func__, desc->devtype);  		}  	}  	return ret_val;  } - -/* fpga_info +/* + * fpga_info   *	front end to fpga_dev_info.  If devnum is invalid, report on all   *	available devices.   */ -int fpga_info( int devnum ) +int fpga_info(int devnum)  { -	if ( devnum == FPGA_INVALID_DEVICE ) { -		if ( next_desc > 0 ) { +	if (devnum == FPGA_INVALID_DEVICE) { +		if (next_desc > 0) {  			int dev; -			for ( dev = 0; dev < next_desc; dev++ ) { -				fpga_dev_info( dev ); -			} +			for (dev = 0; dev < next_desc; dev++) +				fpga_dev_info(dev); +  			return FPGA_SUCCESS;  		} else { -			printf( "%s: No FPGA devices available.\n", __FUNCTION__ ); +			printf("%s: No FPGA devices available.\n", __func__);  			return FPGA_FAIL;  		}  	} -	else return fpga_dev_info( devnum ); -} -/* ------------------------------------------------------------------------- */ +	return fpga_dev_info(devnum); +} diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index 32787b236..49e943718 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -1,4 +1,6 @@  /* + * (C) Copyright 2012-2013, Xilinx, Michal Simek + *   * (C) Copyright 2002   * Rich Ireland, Enterasys Networks, rireland@enterasys.com.   * Keith Outwater, keith_outwater@mvis.com @@ -28,9 +30,11 @@   */  #include <common.h> +#include <fpga.h>  #include <virtex2.h>  #include <spartan2.h>  #include <spartan3.h> +#include <zynqpl.h>  #if 0  #define FPGA_DEBUG @@ -48,6 +52,112 @@ static int xilinx_validate (Xilinx_desc * desc, char *fn);  /* ------------------------------------------------------------------------- */ +int fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +{ +	unsigned int length; +	unsigned int swapsize; +	char buffer[80]; +	unsigned char *dataptr; +	unsigned int i; +	const fpga_desc *desc; +	Xilinx_desc *xdesc; + +	dataptr = (unsigned char *)fpgadata; +	/* Find out fpga_description */ +	desc = fpga_validate(devnum, dataptr, 0, (char *)__func__); +	/* Assign xilinx device description */ +	xdesc = desc->devdesc; + +	/* skip the first bytes of the bitsteam, their meaning is unknown */ +	length = (*dataptr << 8) + *(dataptr + 1); +	dataptr += 2; +	dataptr += length; + +	/* get design name (identifier, length, string) */ +	length = (*dataptr << 8) + *(dataptr + 1); +	dataptr += 2; +	if (*dataptr++ != 0x61) { +		debug("%s: Design name id not recognized in bitstream\n", +		      __func__); +		return FPGA_FAIL; +	} + +	length = (*dataptr << 8) + *(dataptr + 1); +	dataptr += 2; +	for (i = 0; i < length; i++) +		buffer[i] = *dataptr++; + +	printf("  design filename = \"%s\"\n", buffer); + +	/* get part number (identifier, length, string) */ +	if (*dataptr++ != 0x62) { +		printf("%s: Part number id not recognized in bitstream\n", +		       __func__); +		return FPGA_FAIL; +	} + +	length = (*dataptr << 8) + *(dataptr + 1); +	dataptr += 2; +	for (i = 0; i < length; i++) +		buffer[i] = *dataptr++; + +	if (xdesc->name) { +		i = strncmp(buffer, xdesc->name, strlen(xdesc->name)); +		if (i) { +			printf("%s: Wrong bitstream ID for this device\n", +			       __func__); +			printf("%s: Bitstream ID %s, current device ID %d/%s\n", +			       __func__, buffer, devnum, xdesc->name); +			return FPGA_FAIL; +		} +	} else { +		printf("%s: Please fill correct device ID to Xilinx_desc\n", +		       __func__); +	} +	printf("  part number = \"%s\"\n", buffer); + +	/* get date (identifier, length, string) */ +	if (*dataptr++ != 0x63) { +		printf("%s: Date identifier not recognized in bitstream\n", +		       __func__); +		return FPGA_FAIL; +	} + +	length = (*dataptr << 8) + *(dataptr+1); +	dataptr += 2; +	for (i = 0; i < length; i++) +		buffer[i] = *dataptr++; +	printf("  date = \"%s\"\n", buffer); + +	/* get time (identifier, length, string) */ +	if (*dataptr++ != 0x64) { +		printf("%s: Time identifier not recognized in bitstream\n", +		       __func__); +		return FPGA_FAIL; +	} + +	length = (*dataptr << 8) + *(dataptr+1); +	dataptr += 2; +	for (i = 0; i < length; i++) +		buffer[i] = *dataptr++; +	printf("  time = \"%s\"\n", buffer); + +	/* get fpga data length (identifier, length) */ +	if (*dataptr++ != 0x65) { +		printf("%s: Data length id not recognized in bitstream\n", +		       __func__); +		return FPGA_FAIL; +	} +	swapsize = ((unsigned int) *dataptr << 24) + +		   ((unsigned int) *(dataptr + 1) << 16) + +		   ((unsigned int) *(dataptr + 2) << 8) + +		   ((unsigned int) *(dataptr + 3)); +	dataptr += 4; +	printf("  bytes in bitstream = %d\n", swapsize); + +	return fpga_load(devnum, dataptr, swapsize); +} +  int xilinx_load(Xilinx_desc *desc, const void *buf, size_t bsize)  {  	int ret_val = FPGA_FAIL;	/* assume a failure */ @@ -86,6 +196,16 @@ int xilinx_load(Xilinx_desc *desc, const void *buf, size_t bsize)  					__FUNCTION__);  #endif  			break; +		case xilinx_zynq: +#if defined(CONFIG_FPGA_ZYNQPL) +			PRINTF("%s: Launching the Zynq PL Loader...\n", +			       __func__); +			ret_val = zynq_load(desc, buf, bsize); +#else +			printf("%s: No support for Zynq devices.\n", +			       __func__); +#endif +			break;  		default:  			printf ("%s: Unsupported family type, %d\n", @@ -133,6 +253,16 @@ int xilinx_dump(Xilinx_desc *desc, const void *buf, size_t bsize)  					__FUNCTION__);  #endif  			break; +		case xilinx_zynq: +#if defined(CONFIG_FPGA_ZYNQPL) +			PRINTF("%s: Launching the Zynq PL Reader...\n", +			       __func__); +			ret_val = zynq_dump(desc, buf, bsize); +#else +			printf("%s: No support for Zynq devices.\n", +			       __func__); +#endif +			break;  		default:  			printf ("%s: Unsupported family type, %d\n", @@ -158,6 +288,9 @@ int xilinx_info (Xilinx_desc * desc)  		case Xilinx_Virtex2:  			printf ("Virtex-II\n");  			break; +		case xilinx_zynq: +			printf("Zynq PL\n"); +			break;  			/* Add new family types here */  		default:  			printf ("Unknown family type, %d\n", desc->family); @@ -183,6 +316,9 @@ int xilinx_info (Xilinx_desc * desc)  		case master_selectmap:  			printf ("Master SelectMap Mode\n");  			break; +		case devcfg: +			printf("Device configuration interface (Zynq)\n"); +			break;  			/* Add new interface types here */  		default:  			printf ("Unsupported interface type, %d\n", desc->iface); @@ -191,6 +327,8 @@ int xilinx_info (Xilinx_desc * desc)  		printf ("Device Size:   \t%d bytes\n"  				"Cookie:        \t0x%x (%d)\n",  				desc->size, desc->cookie, desc->cookie); +		if (desc->name) +			printf("Device name:   \t%s\n", desc->name);  		if (desc->iface_fns) {  			printf ("Device Function Table @ 0x%p\n", desc->iface_fns); @@ -222,6 +360,14 @@ int xilinx_info (Xilinx_desc * desc)  						__FUNCTION__);  #endif  				break; +			case xilinx_zynq: +#if defined(CONFIG_FPGA_ZYNQPL) +				zynq_info(desc); +#else +				/* just in case */ +				printf("%s: No support for Zynq devices.\n", +				       __func__); +#endif  				/* Add new family types here */  			default:  				/* we don't need a message here - we give one up above */ diff --git a/drivers/fpga/zynqpl.c b/drivers/fpga/zynqpl.c new file mode 100644 index 000000000..8feccdea4 --- /dev/null +++ b/drivers/fpga/zynqpl.c @@ -0,0 +1,355 @@ +/* + * (C) Copyright 2012-2013, Xilinx, Michal Simek + * + * (C) Copyright 2012 + * Joe Hershberger <joe.hershberger@ni.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 <asm/io.h> +#include <zynqpl.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> + +#define DEVCFG_CTRL_PCFG_PROG_B		0x40000000 +#define DEVCFG_ISR_FATAL_ERROR_MASK	0x00740040 +#define DEVCFG_ISR_ERROR_FLAGS_MASK	0x00340840 +#define DEVCFG_ISR_RX_FIFO_OV		0x00040000 +#define DEVCFG_ISR_DMA_DONE		0x00002000 +#define DEVCFG_ISR_PCFG_DONE		0x00000004 +#define DEVCFG_STATUS_DMA_CMD_Q_F	0x80000000 +#define DEVCFG_STATUS_DMA_CMD_Q_E	0x40000000 +#define DEVCFG_STATUS_DMA_DONE_CNT_MASK	0x30000000 +#define DEVCFG_STATUS_PCFG_INIT		0x00000010 +#define DEVCFG_MCTRL_RFIFO_FLUSH	0x00000002 +#define DEVCFG_MCTRL_WFIFO_FLUSH	0x00000001 + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100	/* 10 ms */ +#endif + +#ifndef CONFIG_SYS_FPGA_PROG_TIME +#define CONFIG_SYS_FPGA_PROG_TIME CONFIG_SYS_HZ	/* 1 s */ +#endif + +int zynq_info(Xilinx_desc *desc) +{ +	return FPGA_SUCCESS; +} + +#define DUMMY_WORD	0xffffffff + +/* Xilinx binary format header */ +static const u32 bin_format[] = { +	DUMMY_WORD, /* Dummy words */ +	DUMMY_WORD, +	DUMMY_WORD, +	DUMMY_WORD, +	DUMMY_WORD, +	DUMMY_WORD, +	DUMMY_WORD, +	DUMMY_WORD, +	0x000000bb, /* Sync word */ +	0x11220044, /* Sync word */ +	DUMMY_WORD, +	DUMMY_WORD, +	0xaa995566, /* Sync word */ +}; + +#define SWAP_NO		1 +#define SWAP_DONE	2 + +/* + * Load the whole word from unaligned buffer + * Keep in your mind that it is byte loading on little-endian system + */ +static u32 load_word(const void *buf, u32 swap) +{ +	u32 word = 0; +	u8 *bitc = (u8 *)buf; +	int p; + +	if (swap == SWAP_NO) { +		for (p = 0; p < 4; p++) { +			word <<= 8; +			word |= bitc[p]; +		} +	} else { +		for (p = 3; p >= 0; p--) { +			word <<= 8; +			word |= bitc[p]; +		} +	} + +	return word; +} + +static u32 check_header(const void *buf) +{ +	u32 i, pattern; +	int swap = SWAP_NO; +	u32 *test = (u32 *)buf; + +	debug("%s: Let's check bitstream header\n", __func__); + +	/* Checking that passing bin is not a bitstream */ +	for (i = 0; i < ARRAY_SIZE(bin_format); i++) { +		pattern = load_word(&test[i], swap); + +		/* +		 * Bitstreams in binary format are swapped +		 * compare to regular bistream. +		 * Do not swap dummy word but if swap is done assume +		 * that parsing buffer is binary format +		 */ +		if ((__swab32(pattern) != DUMMY_WORD) && +		    (__swab32(pattern) == bin_format[i])) { +			pattern = __swab32(pattern); +			swap = SWAP_DONE; +			debug("%s: data swapped - let's swap\n", __func__); +		} + +		debug("%s: %d/%x: pattern %x/%x bin_format\n", __func__, i, +		      (u32)&test[i], pattern, bin_format[i]); +		if (pattern != bin_format[i]) { +			debug("%s: Bitstream is not recognized\n", __func__); +			return 0; +		} +	} +	debug("%s: Found bitstream header at %x %s swapinng\n", __func__, +	      (u32)buf, swap == SWAP_NO ? "without" : "with"); + +	return swap; +} + +static void *check_data(u8 *buf, size_t bsize, u32 *swap) +{ +	u32 word, p = 0; /* possition */ + +	/* Because buf doesn't need to be aligned let's read it by chars */ +	for (p = 0; p < bsize; p++) { +		word = load_word(&buf[p], SWAP_NO); +		debug("%s: word %x %x/%x\n", __func__, word, p, (u32)&buf[p]); + +		/* Find the first bitstream dummy word */ +		if (word == DUMMY_WORD) { +			debug("%s: Found dummy word at position %x/%x\n", +			      __func__, p, (u32)&buf[p]); +			*swap = check_header(&buf[p]); +			if (*swap) { +				/* FIXME add full bitstream checking here */ +				return &buf[p]; +			} +		} +		/* Loop can be huge - support CTRL + C */ +		if (ctrlc()) +			return 0; +	} +	return 0; +} + + +int zynq_load(Xilinx_desc *desc, const void *buf, size_t bsize) +{ +	unsigned long ts; /* Timestamp */ +	u32 partialbit = 0; +	u32 i, control, isr_status, status, swap, diff; +	u32 *buf_start; + +	/* Detect if we are going working with partial or full bitstream */ +	if (bsize != desc->size) { +		printf("%s: Working with partial bitstream\n", __func__); +		partialbit = 1; +	} + +	buf_start = check_data((u8 *)buf, bsize, &swap); +	if (!buf_start) +		return FPGA_FAIL; + +	/* Check if data is postpone from start */ +	diff = (u32)buf_start - (u32)buf; +	if (diff) { +		printf("%s: Bitstream is not validated yet (diff %x)\n", +		       __func__, diff); +		return FPGA_FAIL; +	} + +	if ((u32)buf_start & 0x3) { +		u32 *new_buf = (u32 *)((u32)buf & ~0x3); + +		printf("%s: Align buffer at %x to %x(swap %d)\n", __func__, +		       (u32)buf_start, (u32)new_buf, swap); + +		for (i = 0; i < (bsize/4); i++) +			new_buf[i] = load_word(&buf_start[i], swap); + +		swap = SWAP_DONE; +		buf = new_buf; +	} else if (swap != SWAP_DONE) { +		/* For bitstream which are aligned */ +		u32 *new_buf = (u32 *)buf; + +		printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__, +		       swap); + +		for (i = 0; i < (bsize/4); i++) +			new_buf[i] = load_word(&buf_start[i], swap); + +		swap = SWAP_DONE; +	} + +	if (!partialbit) { +		zynq_slcr_devcfg_disable(); + +		/* Setting PCFG_PROG_B signal to high */ +		control = readl(&devcfg_base->ctrl); +		writel(control | DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); +		/* Setting PCFG_PROG_B signal to low */ +		writel(control & ~DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + +		/* Polling the PCAP_INIT status for Reset */ +		ts = get_timer(0); +		while (readl(&devcfg_base->status) & DEVCFG_STATUS_PCFG_INIT) { +			if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { +				printf("%s: Timeout wait for INIT to clear\n", +				       __func__); +				return FPGA_FAIL; +			} +		} + +		/* Setting PCFG_PROG_B signal to high */ +		writel(control | DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + +		/* Polling the PCAP_INIT status for Set */ +		ts = get_timer(0); +		while (!(readl(&devcfg_base->status) & +			DEVCFG_STATUS_PCFG_INIT)) { +			if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { +				printf("%s: Timeout wait for INIT to set\n", +				       __func__); +				return FPGA_FAIL; +			} +		} +	} + +	isr_status = readl(&devcfg_base->int_sts); + +	/* Clear it all, so if Boot ROM comes back, it can proceed */ +	writel(0xFFFFFFFF, &devcfg_base->int_sts); + +	if (isr_status & DEVCFG_ISR_FATAL_ERROR_MASK) { +		debug("%s: Fatal errors in PCAP 0x%X\n", __func__, isr_status); + +		/* If RX FIFO overflow, need to flush RX FIFO first */ +		if (isr_status & DEVCFG_ISR_RX_FIFO_OV) { +			writel(DEVCFG_MCTRL_RFIFO_FLUSH, &devcfg_base->mctrl); +			writel(0xFFFFFFFF, &devcfg_base->int_sts); +		} +		return FPGA_FAIL; +	} + +	status = readl(&devcfg_base->status); + +	debug("%s: Status = 0x%08X\n", __func__, status); + +	if (status & DEVCFG_STATUS_DMA_CMD_Q_F) { +		debug("%s: Error: device busy\n", __func__); +		return FPGA_FAIL; +	} + +	debug("%s: Device ready\n", __func__); + +	if (!(status & DEVCFG_STATUS_DMA_CMD_Q_E)) { +		if (!(readl(&devcfg_base->int_sts) & DEVCFG_ISR_DMA_DONE)) { +			/* Error state, transfer cannot occur */ +			debug("%s: ISR indicates error\n", __func__); +			return FPGA_FAIL; +		} else { +			/* Clear out the status */ +			writel(DEVCFG_ISR_DMA_DONE, &devcfg_base->int_sts); +		} +	} + +	if (status & DEVCFG_STATUS_DMA_DONE_CNT_MASK) { +		/* Clear the count of completed DMA transfers */ +		writel(DEVCFG_STATUS_DMA_DONE_CNT_MASK, &devcfg_base->status); +	} + +	debug("%s: Source = 0x%08X\n", __func__, (u32)buf); +	debug("%s: Size = %zu\n", __func__, bsize); + +	/* Set up the transfer */ +	writel((u32)buf | 1, &devcfg_base->dma_src_addr); +	writel(0xFFFFFFFF, &devcfg_base->dma_dst_addr); +	writel(bsize >> 2, &devcfg_base->dma_src_len); +	writel(0, &devcfg_base->dma_dst_len); + +	isr_status = readl(&devcfg_base->int_sts); + +	/* Polling the PCAP_INIT status for Set */ +	ts = get_timer(0); +	while (!(isr_status & DEVCFG_ISR_DMA_DONE)) { +		if (isr_status & DEVCFG_ISR_ERROR_FLAGS_MASK) { +			debug("%s: Error: isr = 0x%08X\n", __func__, +			      isr_status); +			debug("%s: Write count = 0x%08X\n", __func__, +			      readl(&devcfg_base->write_count)); +			debug("%s: Read count = 0x%08X\n", __func__, +			      readl(&devcfg_base->read_count)); + +			return FPGA_FAIL; +		} +		if (get_timer(ts) > CONFIG_SYS_FPGA_PROG_TIME) { +			printf("%s: Timeout wait for DMA to complete\n", +			       __func__); +			return FPGA_FAIL; +		} +		isr_status = readl(&devcfg_base->int_sts); +	} + +	debug("%s: DMA transfer is done\n", __func__); + +	/* Check FPGA configuration completion */ +	ts = get_timer(0); +	while (!(isr_status & DEVCFG_ISR_PCFG_DONE)) { +		if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { +			printf("%s: Timeout wait for FPGA to config\n", +			       __func__); +			return FPGA_FAIL; +		} +		isr_status = readl(&devcfg_base->int_sts); +	} + +	debug("%s: FPGA config done\n", __func__); + +	/* Clear out the DMA status */ +	writel(DEVCFG_ISR_DMA_DONE, &devcfg_base->int_sts); + +	if (!partialbit) +		zynq_slcr_devcfg_enable(); + +	return FPGA_SUCCESS; +} + +int zynq_dump(Xilinx_desc *desc, const void *buf, size_t bsize) +{ +	return FPGA_FAIL; +} diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 5dbdbe367..72e85a349 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o  COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o  COBJS-$(CONFIG_SH_I2C) += sh_i2c.o  COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o +COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/i2c/zynq_i2c.c b/drivers/i2c/zynq_i2c.c new file mode 100644 index 000000000..ec49660cf --- /dev/null +++ b/drivers/i2c/zynq_i2c.c @@ -0,0 +1,306 @@ +/* + * Driver for the Zynq-7000 PS I2C controller + * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2) + * + * Author: Joe Hershberger <joe.hershberger@ni.com> + * Copyright (c) 2012 Joe Hershberger. + * + * Copyright (c) 2012-2013 Xilinx, Michal Simek + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <i2c.h> +#include <asm/errno.h> +#include <asm/arch/hardware.h> + +/* i2c register set */ +struct zynq_i2c_registers { +	u32 control; +	u32 status; +	u32 address; +	u32 data; +	u32 interrupt_status; +	u32 transfer_size; +	u32 slave_mon_pause; +	u32 time_out; +	u32 interrupt_mask; +	u32 interrupt_enable; +	u32 interrupt_disable; +}; + +/* Control register fields */ +#define ZYNQ_I2C_CONTROL_RW		0x00000001 +#define ZYNQ_I2C_CONTROL_MS		0x00000002 +#define ZYNQ_I2C_CONTROL_NEA		0x00000004 +#define ZYNQ_I2C_CONTROL_ACKEN		0x00000008 +#define ZYNQ_I2C_CONTROL_HOLD		0x00000010 +#define ZYNQ_I2C_CONTROL_SLVMON		0x00000020 +#define ZYNQ_I2C_CONTROL_CLR_FIFO	0x00000040 +#define ZYNQ_I2C_CONTROL_DIV_B_SHIFT	8 +#define ZYNQ_I2C_CONTROL_DIV_B_MASK	0x00003F00 +#define ZYNQ_I2C_CONTROL_DIV_A_SHIFT	14 +#define ZYNQ_I2C_CONTROL_DIV_A_MASK	0x0000C000 + +/* Status register values */ +#define ZYNQ_I2C_STATUS_RXDV	0x00000020 +#define ZYNQ_I2C_STATUS_TXDV	0x00000040 +#define ZYNQ_I2C_STATUS_RXOVF	0x00000080 +#define ZYNQ_I2C_STATUS_BA	0x00000100 + +/* Interrupt register fields */ +#define ZYNQ_I2C_INTERRUPT_COMP		0x00000001 +#define ZYNQ_I2C_INTERRUPT_DATA		0x00000002 +#define ZYNQ_I2C_INTERRUPT_NACK		0x00000004 +#define ZYNQ_I2C_INTERRUPT_TO		0x00000008 +#define ZYNQ_I2C_INTERRUPT_SLVRDY	0x00000010 +#define ZYNQ_I2C_INTERRUPT_RXOVF	0x00000020 +#define ZYNQ_I2C_INTERRUPT_TXOVF	0x00000040 +#define ZYNQ_I2C_INTERRUPT_RXUNF	0x00000080 +#define ZYNQ_I2C_INTERRUPT_ARBLOST	0x00000200 + +#define ZYNQ_I2C_FIFO_DEPTH		16 +#define ZYNQ_I2C_TRANSFERT_SIZE_MAX	255 /* Controller transfer limit */ + +#if defined(CONFIG_ZYNQ_I2C0) +# define ZYNQ_I2C_BASE	ZYNQ_I2C_BASEADDR0 +#else +# define ZYNQ_I2C_BASE	ZYNQ_I2C_BASEADDR1 +#endif + +static struct zynq_i2c_registers *zynq_i2c = +	(struct zynq_i2c_registers *)ZYNQ_I2C_BASE; + +/* I2C init called by cmd_i2c when doing 'i2c reset'. */ +void i2c_init(int requested_speed, int slaveadd) +{ +	/* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */ +	writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) | +		(2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control); + +	/* Enable master mode, ack, and 7-bit addressing */ +	setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS | +		ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA); +} + +#ifdef DEBUG +static void zynq_i2c_debug_status(void) +{ +	int int_status; +	int status; +	int_status = readl(&zynq_i2c->interrupt_status); + +	status = readl(&zynq_i2c->status); +	if (int_status || status) { +		debug("Status: "); +		if (int_status & ZYNQ_I2C_INTERRUPT_COMP) +			debug("COMP "); +		if (int_status & ZYNQ_I2C_INTERRUPT_DATA) +			debug("DATA "); +		if (int_status & ZYNQ_I2C_INTERRUPT_NACK) +			debug("NACK "); +		if (int_status & ZYNQ_I2C_INTERRUPT_TO) +			debug("TO "); +		if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY) +			debug("SLVRDY "); +		if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF) +			debug("RXOVF "); +		if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF) +			debug("TXOVF "); +		if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF) +			debug("RXUNF "); +		if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST) +			debug("ARBLOST "); +		if (status & ZYNQ_I2C_STATUS_RXDV) +			debug("RXDV "); +		if (status & ZYNQ_I2C_STATUS_TXDV) +			debug("TXDV "); +		if (status & ZYNQ_I2C_STATUS_RXOVF) +			debug("RXOVF "); +		if (status & ZYNQ_I2C_STATUS_BA) +			debug("BA "); +		debug("TS%d ", readl(&zynq_i2c->transfer_size)); +		debug("\n"); +	} +} +#endif + +/* Wait for an interrupt */ +static u32 zynq_i2c_wait(u32 mask) +{ +	int timeout, int_status; + +	for (timeout = 0; timeout < 100; timeout++) { +		udelay(100); +		int_status = readl(&zynq_i2c->interrupt_status); +		if (int_status & mask) +			break; +	} +#ifdef DEBUG +	zynq_i2c_debug_status(); +#endif +	/* Clear interrupt status flags */ +	writel(int_status & mask, &zynq_i2c->interrupt_status); + +	return int_status & mask; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +int i2c_probe(u8 dev) +{ +	/* Attempt to read a byte */ +	setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | +		ZYNQ_I2C_CONTROL_RW); +	clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); +	writel(0xFF, &zynq_i2c->interrupt_status); +	writel(dev, &zynq_i2c->address); +	writel(1, &zynq_i2c->transfer_size); + +	return (zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | +		ZYNQ_I2C_INTERRUPT_NACK) & +		ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + */ +int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ +	u32 status; +	u32 i = 0; +	u8 *cur_data = data; + +	/* Check the hardware can handle the requested bytes */ +	if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) +		return -EINVAL; + +	/* Write the register address */ +	setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | +		ZYNQ_I2C_CONTROL_HOLD); +	/* +	 * Temporarily disable restart (by clearing hold) +	 * It doesn't seem to work. +	 */ +	clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW | +		ZYNQ_I2C_CONTROL_HOLD); +	writel(0xFF, &zynq_i2c->interrupt_status); +	while (alen--) +		writel(addr >> (8*alen), &zynq_i2c->data); +	writel(dev, &zynq_i2c->address); + +	/* Wait for the address to be sent */ +	if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { +		/* Release the bus */ +		clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); +		return -ETIMEDOUT; +	} +	debug("Device acked address\n"); + +	setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | +		ZYNQ_I2C_CONTROL_RW); +	/* Start reading data */ +	writel(dev, &zynq_i2c->address); +	writel(length, &zynq_i2c->transfer_size); + +	/* Wait for data */ +	do { +		status = zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | +			ZYNQ_I2C_INTERRUPT_DATA); +		if (!status) { +			/* Release the bus */ +			clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); +			return -ETIMEDOUT; +		} +		debug("Read %d bytes\n", +		      length - readl(&zynq_i2c->transfer_size)); +		for (; i < length - readl(&zynq_i2c->transfer_size); i++) +			*(cur_data++) = readl(&zynq_i2c->data); +	} while (readl(&zynq_i2c->transfer_size) != 0); +	/* All done... release the bus */ +	clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + +#ifdef DEBUG +	zynq_i2c_debug_status(); +#endif +	return 0; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ +	u8 *cur_data = data; + +	/* Write the register address */ +	setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | +		ZYNQ_I2C_CONTROL_HOLD); +	clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); +	writel(0xFF, &zynq_i2c->interrupt_status); +	while (alen--) +		writel(addr >> (8*alen), &zynq_i2c->data); +	/* Start the tranfer */ +	writel(dev, &zynq_i2c->address); +	if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { +		/* Release the bus */ +		clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); +		return -ETIMEDOUT; +	} + +	debug("Device acked address\n"); +	while (length--) { +		writel(*(cur_data++), &zynq_i2c->data); +		if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) { +			if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { +				/* Release the bus */ +				clrbits_le32(&zynq_i2c->control, +					     ZYNQ_I2C_CONTROL_HOLD); +				return -ETIMEDOUT; +			} +		} +	} + +	/* All done... release the bus */ +	clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); +	/* Wait for the address and data to be sent */ +	if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) +		return -ETIMEDOUT; +	return 0; +} + +int i2c_set_bus_num(unsigned int bus) +{ +	/* Only support bus 0 */ +	if (bus > 0) +		return -1; +	return 0; +} + +unsigned int i2c_get_bus_num(void) +{ +	/* Only support bus 0 */ +	return 0; +} diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8cdc3b649..5d869b47a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -28,8 +28,10 @@ LIB	:= $(obj)libmisc.o  COBJS-$(CONFIG_ALI152X) += ali512x.o  COBJS-$(CONFIG_DS4510)  += ds4510.o  COBJS-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o +COBJS-$(CONFIG_FSL_IIM) += fsl_iim.o  COBJS-$(CONFIG_GPIO_LED) += gpio_led.o  COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o +COBJS-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o  COBJS-$(CONFIG_NS87308) += ns87308.o  COBJS-$(CONFIG_PDSP188x) += pdsp188x.o  COBJS-$(CONFIG_STATUS_LED) += status_led.o diff --git a/drivers/misc/fsl_iim.c b/drivers/misc/fsl_iim.c new file mode 100644 index 000000000..9179fbb0d --- /dev/null +++ b/drivers/misc/fsl_iim.c @@ -0,0 +1,286 @@ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx <mmarx@silicontkx.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 <fuse.h> +#include <asm/errno.h> +#include <asm/io.h> +#ifndef CONFIG_MPC512X +#include <asm/arch/imx-regs.h> +#endif + +/* FSL IIM-specific constants */ +#define STAT_BUSY		0x80 +#define STAT_PRGD		0x02 +#define STAT_SNSD		0x01 + +#define STATM_PRGD_M		0x02 +#define STATM_SNSD_M		0x01 + +#define ERR_PRGE		0x80 +#define ERR_WPE			0x40 +#define ERR_OPE			0x20 +#define ERR_RPE			0x10 +#define ERR_WLRE		0x08 +#define ERR_SNSE		0x04 +#define ERR_PARITYE		0x02 + +#define EMASK_PRGE_M		0x80 +#define EMASK_WPE_M		0x40 +#define EMASK_OPE_M		0x20 +#define EMASK_RPE_M		0x10 +#define EMASK_WLRE_M		0x08 +#define EMASK_SNSE_M		0x04 +#define EMASK_PARITYE_M		0x02 + +#define FCTL_DPC		0x80 +#define FCTL_PRG_LENGTH_MASK	0x70 +#define FCTL_ESNS_N		0x08 +#define FCTL_ESNS_0		0x04 +#define FCTL_ESNS_1		0x02 +#define FCTL_PRG		0x01 + +#define UA_A_BANK_MASK		0x38 +#define UA_A_ROWH_MASK		0x07 + +#define LA_A_ROWL_MASK		0xf8 +#define LA_A_BIT_MASK		0x07 + +#define PREV_PROD_REV_MASK	0xf8 +#define PREV_PROD_VT_MASK	0x07 + +/* Select the correct accessors depending on endianness */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define iim_read32		in_le32 +#define iim_write32		out_le32 +#define iim_clrsetbits32	clrsetbits_le32 +#define iim_clrbits32		clrbits_le32 +#define iim_setbits32		setbits_le32 +#elif __BYTE_ORDER == __BIG_ENDIAN +#define iim_read32		in_be32 +#define iim_write32		out_be32 +#define iim_clrsetbits32	clrsetbits_be32 +#define iim_clrbits32		clrbits_be32 +#define iim_setbits32		setbits_be32 +#else +#error Endianess is not defined: please fix to continue +#endif + +/* IIM control registers */ +struct fsl_iim { +	u32 stat; +	u32 statm; +	u32 err; +	u32 emask; +	u32 fctl; +	u32 ua; +	u32 la; +	u32 sdat; +	u32 prev; +	u32 srev; +	u32 prg_p; +	u32 scs[0x1f5]; +	struct { +		u32 word[0x100]; +	} bank[8]; +}; + +static int prepare_access(struct fsl_iim **regs, u32 bank, u32 word, int assert, +				const char *caller) +{ +	*regs = (struct fsl_iim *)IIM_BASE_ADDR; + +	if (bank >= ARRAY_SIZE((*regs)->bank) || +			word >= ARRAY_SIZE((*regs)->bank[0].word) || +			!assert) { +		printf("fsl_iim %s(): Invalid argument\n", caller); +		return -EINVAL; +	} + +	return 0; +} + +static void clear_status(struct fsl_iim *regs) +{ +	iim_setbits32(®s->stat, 0); +	iim_setbits32(®s->err, 0); +} + +static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err) +{ +	*stat = iim_read32(®s->stat); +	*err = iim_read32(®s->err); +	clear_status(regs); +} + +static int prepare_read(struct fsl_iim **regs, u32 bank, u32 word, u32 *val, +			const char *caller) +{ +	int ret; + +	ret = prepare_access(regs, bank, word, val != NULL, caller); +	if (ret) +		return ret; + +	clear_status(*regs); + +	return 0; +} + +int fuse_read(u32 bank, u32 word, u32 *val) +{ +	struct fsl_iim *regs; +	u32 stat, err; +	int ret; + +	ret = prepare_read(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	*val = iim_read32(®s->bank[bank].word[word]); +	finish_access(regs, &stat, &err); + +	if (err & ERR_RPE) { +		puts("fsl_iim fuse_read(): Read protect error\n"); +		return -EIO; +	} + +	return 0; +} + +static void direct_access(struct fsl_iim *regs, u32 bank, u32 word, u32 bit, +				u32 fctl, u32 *stat, u32 *err) +{ +	iim_write32(®s->ua, bank << 3 | word >> 5); +	iim_write32(®s->la, (word << 3 | bit) & 0xff); +	if (fctl == FCTL_PRG) +		iim_write32(®s->prg_p, 0xaa); +	iim_setbits32(®s->fctl, fctl); +	while (iim_read32(®s->stat) & STAT_BUSY) +		udelay(20); +	finish_access(regs, stat, err); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ +	struct fsl_iim *regs; +	u32 stat, err; +	int ret; + +	ret = prepare_read(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	direct_access(regs, bank, word, 0, FCTL_ESNS_N, &stat, &err); + +	if (err & ERR_SNSE) { +		puts("fsl_iim fuse_sense(): Explicit sense cycle error\n"); +		return -EIO; +	} + +	if (!(stat & STAT_SNSD)) { +		puts("fsl_iim fuse_sense(): Explicit sense cycle did not complete\n"); +		return -EIO; +	} + +	*val = iim_read32(®s->sdat); +	return 0; +} + +static int prog_bit(struct fsl_iim *regs, u32 bank, u32 word, u32 bit) +{ +	u32 stat, err; + +	clear_status(regs); +	direct_access(regs, bank, word, bit, FCTL_PRG, &stat, &err); +	iim_write32(®s->prg_p, 0x00); + +	if (err & ERR_PRGE) { +		puts("fsl_iim fuse_prog(): Program error\n"); +		return -EIO; +	} + +	if (err & ERR_WPE) { +		puts("fsl_iim fuse_prog(): Write protect error\n"); +		return -EIO; +	} + +	if (!(stat & STAT_PRGD)) { +		puts("fsl_iim fuse_prog(): Program did not complete\n"); +		return -EIO; +	} + +	return 0; +} + +static int prepare_write(struct fsl_iim **regs, u32 bank, u32 word, u32 val, +				const char *caller) +{ +	return prepare_access(regs, bank, word, !(val & ~0xff), caller); +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ +	struct fsl_iim *regs; +	u32 bit; +	int ret; + +	ret = prepare_write(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	for (bit = 0; val; bit++, val >>= 1) +		if (val & 0x01) { +			ret = prog_bit(regs, bank, word, bit); +			if (ret) +				return ret; +		} + +	return 0; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ +	struct fsl_iim *regs; +	u32 stat, err; +	int ret; + +	ret = prepare_write(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	clear_status(regs); +	iim_write32(®s->bank[bank].word[word], val); +	finish_access(regs, &stat, &err); + +	if (err & ERR_OPE) { +		puts("fsl_iim fuse_override(): Override protect error\n"); +		return -EIO; +	} + +	return 0; +} diff --git a/drivers/misc/mxc_ocotp.c b/drivers/misc/mxc_ocotp.c new file mode 100644 index 000000000..0095b471b --- /dev/null +++ b/drivers/misc/mxc_ocotp.c @@ -0,0 +1,216 @@ +/* + * (C) Copyright 2013 ADVANSEE + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on Dirk Behme's + * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c, + * which is based on Freescale's + * http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6, + * which is: + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * 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 <fuse.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> + +#define BO_CTRL_WR_UNLOCK		16 +#define BM_CTRL_WR_UNLOCK		0xffff0000 +#define BV_CTRL_WR_UNLOCK_KEY		0x3e77 +#define BM_CTRL_ERROR			0x00000200 +#define BM_CTRL_BUSY			0x00000100 +#define BO_CTRL_ADDR			0 +#define BM_CTRL_ADDR			0x0000007f + +#define BO_TIMING_STROBE_READ		16 +#define BM_TIMING_STROBE_READ		0x003f0000 +#define BV_TIMING_STROBE_READ_NS	37 +#define BO_TIMING_RELAX			12 +#define BM_TIMING_RELAX			0x0000f000 +#define BV_TIMING_RELAX_NS		17 +#define BO_TIMING_STROBE_PROG		0 +#define BM_TIMING_STROBE_PROG		0x00000fff +#define BV_TIMING_STROBE_PROG_US	10 + +#define BM_READ_CTRL_READ_FUSE		0x00000001 + +#define BF(value, field)		(((value) << BO_##field) & BM_##field) + +#define WRITE_POSTAMBLE_US		2 + +static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us) +{ +	while (readl(®s->ctrl) & BM_CTRL_BUSY) +		udelay(delay_us); +} + +static void clear_error(struct ocotp_regs *regs) +{ +	writel(BM_CTRL_ERROR, ®s->ctrl_clr); +} + +static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word, +				int assert, const char *caller) +{ +	*regs = (struct ocotp_regs *)OCOTP_BASE_ADDR; + +	if (bank >= ARRAY_SIZE((*regs)->bank) || +			word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 || +			!assert) { +		printf("mxc_ocotp %s(): Invalid argument\n", caller); +		return -EINVAL; +	} + +	enable_ocotp_clk(1); + +	wait_busy(*regs, 1); +	clear_error(*regs); + +	return 0; +} + +static int finish_access(struct ocotp_regs *regs, const char *caller) +{ +	u32 err; + +	err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); +	clear_error(regs); + +	enable_ocotp_clk(0); + +	if (err) { +		printf("mxc_ocotp %s(): Access protect error\n", caller); +		return -EIO; +	} + +	return 0; +} + +static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val, +			const char *caller) +{ +	return prepare_access(regs, bank, word, val != NULL, caller); +} + +int fuse_read(u32 bank, u32 word, u32 *val) +{ +	struct ocotp_regs *regs; +	int ret; + +	ret = prepare_read(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	*val = readl(®s->bank[bank].fuse_regs[word << 2]); + +	return finish_access(regs, __func__); +} + +static void set_timing(struct ocotp_regs *regs) +{ +	u32 ipg_clk; +	u32 relax, strobe_read, strobe_prog; +	u32 timing; + +	ipg_clk = mxc_get_clock(MXC_IPG_CLK); + +	relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; +	strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, +					1000000000) + 2 * (relax + 1) - 1; +	strobe_prog = DIV_ROUND(ipg_clk * BV_TIMING_STROBE_PROG_US, 1000000) + +			2 * (relax + 1) - 1; + +	timing = BF(strobe_read, TIMING_STROBE_READ) | +			BF(relax, TIMING_RELAX) | +			BF(strobe_prog, TIMING_STROBE_PROG); + +	clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX | +			BM_TIMING_STROBE_PROG, timing); +} + +static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word, +				int write) +{ +	u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0; +	u32 addr = bank << 3 | word; + +	set_timing(regs); +	clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR, +			BF(wr_unlock, CTRL_WR_UNLOCK) | +			BF(addr, CTRL_ADDR)); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ +	struct ocotp_regs *regs; +	int ret; + +	ret = prepare_read(®s, bank, word, val, __func__); +	if (ret) +		return ret; + +	setup_direct_access(regs, bank, word, false); +	writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl); +	wait_busy(regs, 1); +	*val = readl(®s->read_fuse_data); + +	return finish_access(regs, __func__); +} + +static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, +				const char *caller) +{ +	return prepare_access(regs, bank, word, true, caller); +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ +	struct ocotp_regs *regs; +	int ret; + +	ret = prepare_write(®s, bank, word, __func__); +	if (ret) +		return ret; + +	setup_direct_access(regs, bank, word, true); +	writel(val, ®s->data); +	wait_busy(regs, BV_TIMING_STROBE_PROG_US); +	udelay(WRITE_POSTAMBLE_US); + +	return finish_access(regs, __func__); +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ +	struct ocotp_regs *regs; +	int ret; + +	ret = prepare_write(®s, bank, word, __func__); +	if (ret) +		return ret; + +	writel(val, ®s->bank[bank].fuse_regs[word << 2]); + +	return finish_access(regs, __func__); +} diff --git a/drivers/misc/twl4030_led.c b/drivers/misc/twl4030_led.c index 33cea116d..e150d8f03 100644 --- a/drivers/misc/twl4030_led.c +++ b/drivers/misc/twl4030_led.c @@ -42,7 +42,7 @@ void twl4030_led_init(unsigned char ledon_mask)  	if (ledon_mask & TWL4030_LED_LEDEN_LEDBON)  		ledon_mask |= TWL4030_LED_LEDEN_LEDBPWM; -	twl4030_i2c_write_u8(TWL4030_CHIP_LED, ledon_mask, -			     TWL4030_LED_LEDEN); +	twl4030_i2c_write_u8(TWL4030_CHIP_LED, TWL4030_LED_LEDEN, +			     ledon_mask);  } diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 2b581781d..24648a293 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -47,6 +47,7 @@ COBJS-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o  COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o  COBJS-$(CONFIG_DWMMC) += dw_mmc.o  COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o +COBJS-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 70a9f91c8..77ebf174f 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -50,6 +50,12 @@  static int initialized = 0; +/* Read Atmel MCI IP version */ +static unsigned int atmel_mci_get_version(struct atmel_mci *mci) +{ +	return readl(&mci->version) & 0x00000fff; +} +  /*   * Print command and status:   * @@ -205,7 +211,10 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	/* Wait for the command to complete */  	while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY))); -	if (status & error_flags) { +	if ((status & error_flags) & MMCI_BIT(RTOE)) { +		dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out"); +		return TIMEOUT; +	} else if (status & error_flags) {  		dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed");  		return COMM_ERR;  	} @@ -297,7 +306,9 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  static void mci_set_ios(struct mmc *mmc)  {  	atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; -	int busw = (mmc->bus_width == 4) ? 1 : 0; +	int bus_width = mmc->bus_width; +	unsigned int version = atmel_mci_get_version(mci); +	int busw;  	/* Set the clock speed */  	mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); @@ -305,9 +316,26 @@ static void mci_set_ios(struct mmc *mmc)  	/*  	 * set the bus width and select slot for this interface  	 * there is no capability for multiple slots on the same interface yet -	 * Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses  	 */ -	writel(MMCI_BF(SCDBUS, busw) | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); +	if ((version & 0xf00) >= 0x300) { +		switch (bus_width) { +		case 8: +			busw = 3; +			break; +		case 4: +			busw = 2; +			break; +		default: +			busw = 0; +			break; +		} + +		writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); +	} else { +		busw = (bus_width == 4) ? 1 : 0; + +		writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); +	}  }  /* Entered into mmc structure during driver init */ @@ -340,9 +368,12 @@ static int mci_init(struct mmc *mmc)  int atmel_mci_init(void *regs)  {  	struct mmc *mmc = malloc(sizeof(struct mmc)); +	struct atmel_mci *mci; +	unsigned int version;  	if (!mmc)  		return -1; +  	strcpy(mmc->name, "mci");  	mmc->priv = regs;  	mmc->send_cmd = mci_send_cmd; @@ -353,7 +384,13 @@ int atmel_mci_init(void *regs)  	/* need to be able to pass these in on a board by board basis */  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; -	mmc->host_caps = MMC_MODE_4BIT; +	mci = (struct atmel_mci *)mmc->priv; +	version = atmel_mci_get_version(mci); +	if ((version & 0xf00) >= 0x300) +		mmc->host_caps = MMC_MODE_8BIT; + +	mmc->host_caps |= MMC_MODE_4BIT; +  	/*  	 * min and max frequencies determined by  	 * max and min of clock divider diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index a89660f13..fdaf9c763 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -41,7 +41,7 @@  #include <asm/arch/clock.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/sys_proto.h> -#include <asm/arch/dma.h> +#include <asm/imx-common/dma.h>  #include <bouncebuf.h>  struct mxsmmc_priv { diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 166744c32..afdfa886e 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -29,7 +29,7 @@  #include <i2c.h>  #include <twl4030.h>  #include <twl6030.h> -#include <twl6035.h> +#include <palmas.h>  #include <asm/gpio.h>  #include <asm/io.h>  #include <asm/arch/mmc_host_def.h> @@ -107,7 +107,7 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)  }  #endif -#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER)  static void omap5_pbias_config(struct mmc *mmc)  {  	u32 value = 0; @@ -117,7 +117,7 @@ static void omap5_pbias_config(struct mmc *mmc)  	value |= SDCARD_BIAS_HIZ_MODE;  	writel(value, (*ctrl)->control_pbias); -	twl6035_mmc1_poweron_ldo(); +	palmas_mmc1_poweron_ldo();  	value = readl((*ctrl)->control_pbias);  	value &= ~SDCARD_BIAS_HIZ_MODE; @@ -178,7 +178,7 @@ unsigned char mmc_board_init(struct mmc *mmc)  	if (mmc->block_dev.dev == 0)  		omap4_vmmc_pbias_config(mmc);  #endif -#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER)  	if (mmc->block_dev.dev == 0)  		omap5_pbias_config(mmc);  #endif diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c new file mode 100644 index 000000000..9e37af45f --- /dev/null +++ b/drivers/mmc/zynq_sdhci.c @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2013 Inc. + * + * Xilinx Zynq SD Host Controller Interface + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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 <sdhci.h> +#include <asm/arch/sys_proto.h> + +int zynq_sdhci_init(u32 regbase) +{ +	struct sdhci_host *host = NULL; + +	host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); +	if (!host) { +		printf("zynq_sdhci_init: sdhci_host malloc fail\n"); +		return 1; +	} + +	host->name = "zynq_sdhci"; +	host->ioaddr = (void *)regbase; +	host->quirks = SDHCI_QUIRK_NO_CD | SDHCI_QUIRK_WAIT_SEND_CMD; +	host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + +	host->host_caps = MMC_MODE_HC; + +	add_sdhci(host, 52000000, 52000000 >> 9); +	return 0; +} diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 22d84407d..25f875202 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -38,6 +38,7 @@  #include <asm/processor.h>  #include <asm/io.h>  #include <asm/byteorder.h> +#include <asm/unaligned.h>  #include <environment.h>  #include <mtd/cfi_flash.h>  #include <watchdog.h> @@ -183,16 +184,16 @@ u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));  flash_info_t *flash_get_info(ulong base)  {  	int i; -	flash_info_t *info = NULL; +	flash_info_t *info;  	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { -		info = & flash_info[i]; +		info = &flash_info[i];  		if (info->size && info->start[0] <= base &&  		    base <= info->start[0] + info->size - 1) -			break; +			return info;  	} -	return info; +	return NULL;  }  #endif @@ -1640,9 +1641,10 @@ static void cfi_reverse_geometry(struct cfi_qry *qry)  	u32 tmp;  	for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) { -		tmp = qry->erase_region_info[i]; -		qry->erase_region_info[i] = qry->erase_region_info[j]; -		qry->erase_region_info[j] = tmp; +		tmp = get_unaligned(&(qry->erase_region_info[i])); +		put_unaligned(get_unaligned(&(qry->erase_region_info[j])), +			      &(qry->erase_region_info[i])); +		put_unaligned(tmp, &(qry->erase_region_info[j]));  	}  } @@ -2073,8 +2075,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)  	info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE);  	if (flash_detect_cfi (info, &qry)) { -		info->vendor = le16_to_cpu(qry.p_id); -		info->ext_addr = le16_to_cpu(qry.p_adr); +		info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id))); +		info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr)));  		num_erase_regions = qry.num_erase_regions;  		if (info->ext_addr) { @@ -2163,7 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)  				break;  			} -			tmp = le32_to_cpu(qry.erase_region_info[i]); +			tmp = le32_to_cpu(get_unaligned( +						&(qry.erase_region_info[i])));  			debug("erase region %u: 0x%08lx\n", i, tmp);  			erase_region_count = (tmp & 0xffff) + 1; diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 35769c5ea..882170491 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -34,6 +34,7 @@ NORMAL_DRIVERS=y  endif  COBJS-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +COBJS-$(CONFIG_SPL_NAND_DOCG4) += docg4_spl.o  COBJS-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o  COBJS-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o  COBJS-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o @@ -77,6 +78,7 @@ 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 +COBJS-$(CONFIG_NAND_DOCG4) += docg4.o  else  # minimal SPL drivers diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c new file mode 100644 index 000000000..7dd9953be --- /dev/null +++ b/drivers/mtd/nand/docg4.c @@ -0,0 +1,1028 @@ +/* + * drivers/mtd/nand/docg4.c + * + * Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * Tested on the Palm Treo 680.  The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well.  Let me know! + * + * TODO: + * + *  Mechanism for management of password-protected areas + * + *  Hamming ecc when reading oob only + * + *  According to the M-Sys documentation, this device is also available in a + *  "dual-die" configuration having a 256MB capacity, but no mechanism for + *  detecting this variant is documented.  Currently this driver assumes 128MB + *  capacity. + * + *  Support for multiple cascaded devices ("floors").  Not sure which gadgets + *  contain multiple G4s in a cascaded configuration, if any. + * + */ + + +#include <common.h> +#include <asm/arch/hardware.h> +#include <asm/io.h> +#include <asm/bitops.h> +#include <asm/errno.h> +#include <malloc.h> +#include <nand.h> +#include <linux/bch.h> +#include <linux/bitrev.h> +#include <linux/mtd/docg4.h> + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays.  But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ +	writew(0, docptr + DOC_NOP); +} + + +static int poll_status(void __iomem *docptr) +{ +	/* +	 * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL +	 * register.  Operations known to take a long time (e.g., block erase) +	 * should sleep for a while before calling this. +	 */ + +	uint8_t flash_status; + +	/* hardware quirk requires reading twice initially */ +	flash_status = readb(docptr + DOC_FLASHCONTROL); + +	do { +		flash_status = readb(docptr + DOC_FLASHCONTROL); +	} while (!(flash_status & DOC_CTRL_FLASHREADY)); + +	return 0; +} + +static void write_addr(void __iomem *docptr, uint32_t docg4_addr) +{ +	/* write the four address bytes packed in docg4_addr to the device */ + +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +/* + * This is a module parameter in the linux kernel version of this driver.  It is + * hard-coded to 'off' for u-boot.  This driver uses oob to mark bad blocks. + * This can be problematic when dealing with data not intended for the mtd/nand + * subsystem.  For example, on boards that boot from the docg4 and use the IPL + * to load an spl + u-boot image, the blocks containing the image will be + * reported as "bad" because the oob of the first page of each block contains a + * magic number that the IPL looks for, which causes the badblock scan to + * erroneously add them to the bad block table.  To erase such a block, use + * u-boot's 'nand scrub'.  scrub is safe for the docg4.  The device does have a + * factory bad block table, but it is read-only, and is used in conjunction with + * oob bad block markers that are written by mtd/nand when a block is deemed to + * be bad.  To read data from "bad" blocks, use 'read.raw'.  Unfortunately, + * read.raw does not use ecc, which would still work fine on such misidentified + * bad blocks.  TODO: u-boot nand utilities need the ability to ignore bad + * blocks. + */ +static const int ignore_badblocks; /* remains false */ + +struct docg4_priv { +	int status; +	struct { +		unsigned int command; +		int column; +		int page; +	} last_command; +	uint8_t oob_buf[16]; +	uint8_t ecc_buf[7]; +	int oob_page; +	struct bch_control *bch; +}; +/* + * Oob bytes 0 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 bytes.  Bytes 8 - 14 are hw-generated ecc. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static struct nand_ecclayout docg4_oobinfo = { +	.eccbytes = 9, +	.eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, +	.oobavail = 7, +	.oobfree = { {0, 7} } +}; + +static void reset(void __iomem *docptr) +{ +	/* full device reset */ + +	writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, docptr + DOC_ASICMODE); +	writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), +	       docptr + DOC_ASICMODECONFIRM); +	write_nop(docptr); + +	writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, +	       docptr + DOC_ASICMODE); +	writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), +	       docptr + DOC_ASICMODECONFIRM); + +	writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + +	poll_status(docptr); +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ +	/* +	 * Select among multiple cascaded chips ("floors").  Multiple floors are +	 * not yet supported, so the only valid non-negative value is 0. +	 */ +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; + +	if (chip < 0) +		return;		/* deselected */ + +	if (chip > 0) +		printf("multiple floors currently unsupported\n"); + +	writew(0, docptr + DOC_DEVICESELECT); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ +	/* read the 7 hw-generated ecc bytes */ + +	int i; +	for (i = 0; i < 7; i++) { /* hw quirk; read twice */ +		ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); +		ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); +	} +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ +	/* +	 * Called after a page read when hardware reports bitflips. +	 * Up to four bitflips can be corrected. +	 */ + +	struct nand_chip *nand = mtd->priv; +	struct docg4_priv *doc = nand->priv; +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	int i, numerrs; +	unsigned int errpos[4]; +	const uint8_t blank_read_hwecc[8] = { +		0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + +	read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + +	/* check if read error is due to a blank page */ +	if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) +		return 0;	/* yes */ + +	/* skip additional check of "written flag" if ignore_badblocks */ +	if (!ignore_badblocks) { +		/* +		 * If the hw ecc bytes are not those of a blank page, there's +		 * still a chance that the page is blank, but was read with +		 * errors.  Check the "written flag" in last oob byte, which +		 * is set to zero when a page is written.  If more than half +		 * the bits are set, assume a blank page.  Unfortunately, the +		 * bit flips(s) are not reported in stats. +		 */ + +		if (doc->oob_buf[15]) { +			int bit, numsetbits = 0; +			unsigned long written_flag = doc->oob_buf[15]; + +			for (bit = 0; bit < 8; bit++) { +				if (written_flag & 0x01) +					numsetbits++; +				written_flag >>= 1; +			} +			if (numsetbits > 4) { /* assume blank */ +				printf("errors in blank page at offset %08x\n", +				       page * DOCG4_PAGE_SIZE); +				return 0; +			} +		} +	} + +	/* +	 * The hardware ecc unit produces oob_ecc ^ calc_ecc.  The kernel's bch +	 * algorithm is used to decode this.  However the hw operates on page +	 * data in a bit order that is the reverse of that of the bch alg, +	 * requiring that the bits be reversed on the result.  Thanks to Ivan +	 * Djelic for his analysis! +	 */ +	for (i = 0; i < 7; i++) +		doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + +	numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, +			     doc->ecc_buf, NULL, errpos); + +	if (numerrs == -EBADMSG) { +		printf("uncorrectable errors at offset %08x\n", +		       page * DOCG4_PAGE_SIZE); +		return -EBADMSG; +	} + +	BUG_ON(numerrs < 0);	/* -EINVAL, or anything other than -EBADMSG */ + +	/* undo last step in BCH alg (modulo mirroring not needed) */ +	for (i = 0; i < numerrs; i++) +		errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + +	/* fix the errors */ +	for (i = 0; i < numerrs; i++) { +		/* ignore if error within oob ecc bytes */ +		if (errpos[i] > DOCG4_USERDATA_LEN * 8) +			continue; + +		/* if error within oob area preceeding ecc bytes... */ +		if (errpos[i] > DOCG4_PAGE_SIZE * 8) +			__change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, +				     (unsigned long *)doc->oob_buf); + +		else    /* error in page data */ +			__change_bit(errpos[i], (unsigned long *)buf); +	} + +	printf("%d error(s) corrected at offset %08x\n", +	       numerrs, page * DOCG4_PAGE_SIZE); + +	return numerrs; +} + +static int read_progstatus(struct docg4_priv *doc, void __iomem *docptr) +{ +	/* +	 * This apparently checks the status of programming.  Done after an +	 * erasure, and after page data is written.  On error, the status is +	 * saved, to be later retrieved by the nand infrastructure code. +	 */ + +	/* status is read from the I/O reg */ +	uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); +	uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); +	uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "docg4: %s: %02x %02x %02x\n", +	    __func__, status1, status2, status3); + +	if (status1 != DOCG4_PROGSTATUS_GOOD || +	    status2 != DOCG4_PROGSTATUS_GOOD_2 || +	    status3 != DOCG4_PROGSTATUS_GOOD_2) { +		doc->status = NAND_STATUS_FAIL; +		printf("read_progstatus failed: %02x, %02x, %02x\n", +		       status1, status2, status3); +		return -EIO; +	} +	return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ +	/* +	 * Final step in writing a page.  Writes the contents of its +	 * internal buffer out to the flash array, or some such. +	 */ + +	struct nand_chip *nand = mtd->priv; +	struct docg4_priv *doc = nand->priv; +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	int retval = 0; + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "docg4: %s\n", __func__); + +	writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); +	writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); + +	/* Just busy-wait; usleep_range() slows things down noticeably. */ +	poll_status(docptr); + +	writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); +	writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); +	writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); + +	retval = read_progstatus(doc, docptr); +	writew(0, docptr + DOC_DATAEND); +	write_nop(docptr); +	poll_status(docptr); +	write_nop(docptr); + +	return retval; +} + +static void sequence_reset(void __iomem *docptr) +{ +	/* common starting sequence for all operations */ + +	writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); +	writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); +	writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); +	poll_status(docptr); +	write_nop(docptr); +} + +static void read_page_prologue(void __iomem *docptr, uint32_t docg4_addr) +{ +	/* first step in reading a page */ + +	sequence_reset(docptr); + +	writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); +	writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); + +	write_addr(docptr, docg4_addr); + +	write_nop(docptr); +	writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); + +	poll_status(docptr); +} + +static void write_page_prologue(void __iomem *docptr, uint32_t docg4_addr) +{ +	/* first step in writing a page */ + +	sequence_reset(docptr); +	writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); +	writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_addr(docptr, docg4_addr); +	write_nop(docptr); +	write_nop(docptr); +	poll_status(docptr); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ +	/* +	 * Convert mtd address to format used by the device, 32 bit packed. +	 * +	 * Some notes on G4 addressing... The M-Sys documentation on this device +	 * claims that pages are 2K in length, and indeed, the format of the +	 * address used by the device reflects that.  But within each page are +	 * four 512 byte "sub-pages", each with its own oob data that is +	 * read/written immediately after the 512 bytes of page data.  This oob +	 * data contains the ecc bytes for the preceeding 512 bytes. +	 * +	 * Rather than tell the mtd nand infrastructure that page size is 2k, +	 * with four sub-pages each, we engage in a little subterfuge and tell +	 * the infrastructure code that pages are 512 bytes in size.  This is +	 * done because during the course of reverse-engineering the device, I +	 * never observed an instance where an entire 2K "page" was read or +	 * written as a unit.  Each "sub-page" is always addressed individually, +	 * its data read/written, and ecc handled before the next "sub-page" is +	 * addressed. +	 * +	 * This requires us to convert addresses passed by the mtd nand +	 * infrastructure code to those used by the device. +	 * +	 * The address that is written to the device consists of four bytes: the +	 * first two are the 2k page number, and the second is the index into +	 * the page.  The index is in terms of 16-bit half-words and includes +	 * the preceeding oob data, so e.g., the index into the second +	 * "sub-page" is 0x108, and the full device address of the start of mtd +	 * page 0x201 is 0x00800108. +	 */ +	int g4_page = page / 4;	                      /* device's 2K page */ +	int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ +	return (g4_page << 16) | g4_index;	      /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, +			  int page_addr) +{ +	/* handle standard nand commands */ + +	struct nand_chip *nand = mtd->priv; +	struct docg4_priv *doc = nand->priv; +	uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s %x, page_addr=%x, column=%x\n", +	    __func__, command, page_addr, column); + +	/* +	 * Save the command and its arguments.  This enables emulation of +	 * standard flash devices, and also some optimizations. +	 */ +	doc->last_command.command = command; +	doc->last_command.column = column; +	doc->last_command.page = page_addr; + +	switch (command) { +	case NAND_CMD_RESET: +		reset(CONFIG_SYS_NAND_BASE); +		break; + +	case NAND_CMD_READ0: +		read_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); +		break; + +	case NAND_CMD_STATUS: +		/* next call to read_byte() will expect a status */ +		break; + +	case NAND_CMD_SEQIN: +		write_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); + +		/* hack for deferred write of oob bytes */ +		if (doc->oob_page == page_addr) +			memcpy(nand->oob_poi, doc->oob_buf, 16); +		break; + +	case NAND_CMD_PAGEPROG: +		pageprog(mtd); +		break; + +	/* we don't expect these, based on review of nand_base.c */ +	case NAND_CMD_READOOB: +	case NAND_CMD_READID: +	case NAND_CMD_ERASE1: +	case NAND_CMD_ERASE2: +		printf("docg4_command: unexpected nand command 0x%x\n", +		       command); +		break; +	} +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ +	int i; +	struct nand_chip *nand = mtd->priv; +	uint16_t *p = (uint16_t *)buf; +	len >>= 1; + +	for (i = 0; i < len; i++) +		p[i] = readw(nand->IO_ADDR_R); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, +			  int page, int sndcmd) +{ +	struct docg4_priv *doc = nand->priv; +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	uint16_t status; + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page %x\n", __func__, page); + +	/* +	 * Oob bytes are read as part of a normal page read.  If the previous +	 * nand command was a read of the page whose oob is now being read, just +	 * copy the oob bytes that we saved in a local buffer and avoid a +	 * separate oob read. +	 */ +	if (doc->last_command.command == NAND_CMD_READ0 && +	    doc->last_command.page == page) { +		memcpy(nand->oob_poi, doc->oob_buf, 16); +		return 0; +	} + +	/* +	 * Separate read of oob data only. +	 */ +	docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + +	writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); + +	/* the 1st byte from the I/O reg is a status; the rest is oob data */ +	status = readw(docptr + DOC_IOSPACE_DATA); +	if (status & DOCG4_READ_ERROR) { +		printf("docg4_read_oob failed: status = 0x%02x\n", status); +		return -EIO; +	} + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: status = 0x%x\n", __func__, status); + +	docg4_read_buf(mtd, nand->oob_poi, 16); + +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	writew(0, docptr + DOC_DATAEND); +	write_nop(docptr); + +	return 0; +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, +			   int page) +{ +	/* +	 * Writing oob-only is not really supported, because MLC nand must write +	 * oob bytes at the same time as page data.  Nonetheless, we save the +	 * oob buffer contents here, and then write it along with the page data +	 * if the same page is subsequently written.  This allows user space +	 * utilities that write the oob data prior to the page data to work +	 * (e.g., nandwrite).  The disdvantage is that, if the intention was to +	 * write oob only, the operation is quietly ignored.  Also, oob can get +	 * corrupted if two concurrent processes are running nandwrite. +	 */ + +	/* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ +	struct docg4_priv *doc = nand->priv; +	doc->oob_page = page; +	memcpy(doc->oob_buf, nand->oob_poi, 16); +	return 0; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ +	/* only called when module_param ignore_badblocks is set */ +	return 0; +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ +	int i; +	struct nand_chip *nand = mtd->priv; +	uint16_t *p = (uint16_t *)buf; +	len >>= 1; + +	for (i = 0; i < len; i++) +		writew(p[i], nand->IO_ADDR_W); +} + +static void write_page(struct mtd_info *mtd, struct nand_chip *nand, +		       const uint8_t *buf, int use_ecc) +{ +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	uint8_t ecc_buf[8]; + +	writew(DOC_ECCCONF0_ECC_ENABLE | +	       DOC_ECCCONF0_UNKNOWN | +	       DOCG4_BCH_SIZE, +	       docptr + DOC_ECCCONF0); +	write_nop(docptr); + +	/* write the page data */ +	docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + +	/* oob bytes 0 through 5 are written to I/O reg */ +	docg4_write_buf16(mtd, nand->oob_poi, 6); + +	/* oob byte 6 written to a separate reg */ +	writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + +	write_nop(docptr); +	write_nop(docptr); + +	/* write hw-generated ecc bytes to oob */ +	if (likely(use_ecc)) { +		/* oob byte 7 is hamming code */ +		uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); +		hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ +		writew(hamming, docptr + DOCG4_OOB_6_7); +		write_nop(docptr); + +		/* read the 7 bch bytes from ecc regs */ +		read_hw_ecc(docptr, ecc_buf); +		ecc_buf[7] = 0;         /* clear the "page written" flag */ +	} + +	/* write user-supplied bytes to oob */ +	else { +		writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); +		write_nop(docptr); +		memcpy(ecc_buf, &nand->oob_poi[8], 8); +	} + +	docg4_write_buf16(mtd, ecc_buf, 8); +	write_nop(docptr); +	write_nop(docptr); +	writew(0, docptr + DOC_DATAEND); +	write_nop(docptr); +} + +static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, +				 const uint8_t *buf) +{ +	return write_page(mtd, nand, buf, 0); +} + +static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, +			     const uint8_t *buf) +{ +	return write_page(mtd, nand, buf, 1); +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, +		     uint8_t *buf, int page, int use_ecc) +{ +	struct docg4_priv *doc = nand->priv; +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	uint16_t status, edc_err, *buf16; + +	writew(DOC_ECCCONF0_READ_MODE | +	       DOC_ECCCONF0_ECC_ENABLE | +	       DOC_ECCCONF0_UNKNOWN | +	       DOCG4_BCH_SIZE, +	       docptr + DOC_ECCCONF0); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); + +	/* the 1st byte from the I/O reg is a status; the rest is page data */ +	status = readw(docptr + DOC_IOSPACE_DATA); +	if (status & DOCG4_READ_ERROR) { +		printf("docg4_read_page: bad status: 0x%02x\n", status); +		writew(0, docptr + DOC_DATAEND); +		return -EIO; +	} + +	docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + +	/* first 14 oob bytes read from I/O reg */ +	docg4_read_buf(mtd, nand->oob_poi, 14); + +	/* last 2 read from another reg */ +	buf16 = (uint16_t *)(nand->oob_poi + 14); +	*buf16 = readw(docptr + DOCG4_MYSTERY_REG); + +	/* +	 * Diskonchips read oob immediately after a page read.  Mtd +	 * infrastructure issues a separate command for reading oob after the +	 * page is read.  So we save the oob bytes in a local buffer and just +	 * copy it if the next command reads oob from the same page. +	 */ +	memcpy(doc->oob_buf, nand->oob_poi, 16); + +	write_nop(docptr); + +	if (likely(use_ecc)) { +		/* read the register that tells us if bitflip(s) detected  */ +		edc_err = readw(docptr + DOC_ECCCONF1); +		edc_err = readw(docptr + DOC_ECCCONF1); + +		/* If bitflips are reported, attempt to correct with ecc */ +		if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { +			int bits_corrected = correct_data(mtd, buf, page); +			if (bits_corrected == -EBADMSG) +				mtd->ecc_stats.failed++; +			else +				mtd->ecc_stats.corrected += bits_corrected; +		} +	} + +	writew(0, docptr + DOC_DATAEND); +	return 0; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, +			       uint8_t *buf, int page) +{ +	return read_page(mtd, nand, buf, page, 0); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, +			   uint8_t *buf, int page) +{ +	return read_page(mtd, nand, buf, page, 1); +} + +static void docg4_erase_block(struct mtd_info *mtd, int page) +{ +	struct nand_chip *nand = mtd->priv; +	struct docg4_priv *doc = nand->priv; +	void __iomem *docptr = CONFIG_SYS_NAND_BASE; +	uint16_t g4_page; + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page %04x\n", __func__, page); + +	sequence_reset(docptr); + +	writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); +	writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); + +	/* only 2 bytes of address are written to specify erase block */ +	g4_page = (uint16_t)(page / 4);  /* to g4's 2k page addressing */ +	writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); +	g4_page >>= 8; +	writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); +	write_nop(docptr); + +	/* start the erasure */ +	writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); + +	poll_status(docptr); +	writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); +	writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); +	writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); + +	read_progstatus(doc, docptr); + +	writew(0, docptr + DOC_DATAEND); +	write_nop(docptr); +	poll_status(docptr); +	write_nop(docptr); +} + +static int read_factory_bbt(struct mtd_info *mtd) +{ +	/* +	 * The device contains a read-only factory bad block table.  Read it and +	 * update the memory-based bbt accordingly. +	 */ + +	struct nand_chip *nand = mtd->priv; +	uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); +	uint8_t *buf; +	int i, block, status; + +	buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); +	if (buf == NULL) +		return -ENOMEM; + +	read_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); +	status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); +	if (status) +		goto exit; + +	/* +	 * If no memory-based bbt was created, exit.  This will happen if module +	 * parameter ignore_badblocks is set.  Then why even call this function? +	 * For an unknown reason, block erase always fails if it's the first +	 * operation after device power-up.  The above read ensures it never is. +	 * Ugly, I know. +	 */ +	if (nand->bbt == NULL)  /* no memory-based bbt */ +		goto exit; + +	/* +	 * Parse factory bbt and update memory-based bbt.  Factory bbt format is +	 * simple: one bit per block, block numbers increase left to right (msb +	 * to lsb).  Bit clear means bad block. +	 */ +	for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { +		int bitnum; +		uint8_t mask; +		for (bitnum = 0, mask = 0x80; +		     bitnum < 8; bitnum++, mask >>= 1) { +			if (!(buf[i] & mask)) { +				int badblock = block + bitnum; +				nand->bbt[badblock / 4] |= +					0x03 << ((badblock % 4) * 2); +				mtd->ecc_stats.badblocks++; +				printf("factory-marked bad block: %d\n", +				       badblock); +			} +		} +	} + exit: +	kfree(buf); +	return status; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ +	/* +	 * Mark a block as bad.  Bad blocks are marked in the oob area of the +	 * first page of the block.  The default scan_bbt() in the nand +	 * infrastructure code works fine for building the memory-based bbt +	 * during initialization, as does the nand infrastructure function that +	 * checks if a block is bad by reading the bbt.  This function replaces +	 * the nand default because writes to oob-only are not supported. +	 */ + +	int ret, i; +	uint8_t *buf; +	struct nand_chip *nand = mtd->priv; +	struct nand_bbt_descr *bbtd = nand->badblock_pattern; +	int block = (int)(ofs >> nand->bbt_erase_shift); +	int page = (int)(ofs >> nand->page_shift); +	uint32_t g4_addr = mtd_to_docg4_address(page, 0); + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: %08llx\n", __func__, ofs); + +	if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) +		printf("%s: ofs %llx not start of block!\n", +		       __func__, ofs); + +	/* allocate blank buffer for page data */ +	buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); +	if (buf == NULL) +		return -ENOMEM; + +	/* update bbt in memory */ +	nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); + +	/* write bit-wise negation of pattern to oob buffer */ +	memset(nand->oob_poi, 0xff, mtd->oobsize); +	for (i = 0; i < bbtd->len; i++) +		nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + +	/* write first page of block */ +	write_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); +	docg4_write_page(mtd, nand, buf); +	ret = pageprog(mtd); +	if (!ret) +		mtd->ecc_stats.badblocks++; + +	kfree(buf); + +	return ret; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ +	struct nand_chip *nand = mtd->priv; +	struct docg4_priv *doc = nand->priv; + +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s\n", __func__); + +	if (doc->last_command.command == NAND_CMD_STATUS) { +		int status; + +		/* +		 * Previous nand command was status request, so nand +		 * infrastructure code expects to read the status here.  If an +		 * error occurred in a previous operation, report it. +		 */ +		doc->last_command.command = 0; + +		if (doc->status) { +			status = doc->status; +			doc->status = 0; +		} + +		/* why is NAND_STATUS_WP inverse logic?? */ +		else +			status = NAND_STATUS_WP | NAND_STATUS_READY; + +		return status; +	} + +	printf("unexpectd call to read_byte()\n"); + +	return 0; +} + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ +	struct docg4_priv *doc = nand->priv; +	int status = NAND_STATUS_WP;       /* inverse logic?? */ +	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s...\n", __func__); + +	/* report any previously unreported error */ +	if (doc->status) { +		status |= doc->status; +		doc->status = 0; +		return status; +	} + +	status |= poll_status(CONFIG_SYS_NAND_BASE); +	return status; +} + +int docg4_nand_init(struct mtd_info *mtd, struct nand_chip *nand, int devnum) +{ +	uint16_t id1, id2; +	struct docg4_priv *docg4; +	int retval; + +	docg4 = kzalloc(sizeof(*docg4), GFP_KERNEL); +	if (!docg4) +		return -1; + +	mtd->priv = nand; +	nand->priv = docg4; + +	/* These must be initialized here because the docg4 is non-standard +	 * and doesn't produce an id that the nand code can use to look up +	 * these values (nand_scan_ident() not called). +	 */ +	mtd->size = DOCG4_CHIP_SIZE; +	mtd->name = "Msys_Diskonchip_G4"; +	mtd->writesize = DOCG4_PAGE_SIZE; +	mtd->erasesize = DOCG4_BLOCK_SIZE; +	mtd->oobsize = DOCG4_OOB_SIZE; + +	nand->IO_ADDR_R = +		(void __iomem *)CONFIG_SYS_NAND_BASE + DOC_IOSPACE_DATA; +	nand->IO_ADDR_W = nand->IO_ADDR_R; +	nand->chipsize = DOCG4_CHIP_SIZE; +	nand->chip_shift = DOCG4_CHIP_SHIFT; +	nand->bbt_erase_shift = DOCG4_ERASE_SHIFT; +	nand->phys_erase_shift = DOCG4_ERASE_SHIFT; +	nand->chip_delay = 20; +	nand->page_shift = DOCG4_PAGE_SHIFT; +	nand->pagemask = 0x3ffff; +	nand->badblockpos = NAND_LARGE_BADBLOCK_POS; +	nand->badblockbits = 8; +	nand->ecc.layout = &docg4_oobinfo; +	nand->ecc.mode = NAND_ECC_HW_SYNDROME; +	nand->ecc.size = DOCG4_PAGE_SIZE; +	nand->ecc.prepad = 8; +	nand->ecc.bytes	= 8; +	nand->options = +		NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; +	nand->controller = &nand->hwcontrol; + +	/* methods */ +	nand->cmdfunc = docg4_command; +	nand->waitfunc = docg4_wait; +	nand->select_chip = docg4_select_chip; +	nand->read_byte = docg4_read_byte; +	nand->block_markbad = docg4_block_markbad; +	nand->read_buf = docg4_read_buf; +	nand->write_buf = docg4_write_buf16; +	nand->scan_bbt = nand_default_bbt; +	nand->erase_cmd = docg4_erase_block; +	nand->ecc.read_page = docg4_read_page; +	nand->ecc.write_page = docg4_write_page; +	nand->ecc.read_page_raw = docg4_read_page_raw; +	nand->ecc.write_page_raw = docg4_write_page_raw; +	nand->ecc.read_oob = docg4_read_oob; +	nand->ecc.write_oob = docg4_write_oob; + +	/* +	 * The way the nand infrastructure code is written, a memory-based bbt +	 * is not created if NAND_SKIP_BBTSCAN is set.  With no memory bbt, +	 * nand->block_bad() is used.  So when ignoring bad blocks, we skip the +	 * scan and define a dummy block_bad() which always returns 0. +	 */ +	if (ignore_badblocks) { +		nand->options |= NAND_SKIP_BBTSCAN; +		nand->block_bad	= docg4_block_neverbad; +	} + +	reset(CONFIG_SYS_NAND_BASE); + +	/* check for presence of g4 chip by reading id registers */ +	id1 = readw(CONFIG_SYS_NAND_BASE + DOC_CHIPID); +	id1 = readw(CONFIG_SYS_NAND_BASE + DOCG4_MYSTERY_REG); +	id2 = readw(CONFIG_SYS_NAND_BASE + DOC_CHIPID_INV); +	id2 = readw(CONFIG_SYS_NAND_BASE + DOCG4_MYSTERY_REG); +	if (id1 != DOCG4_IDREG1_VALUE || id2 != DOCG4_IDREG2_VALUE) +		return -1; + +	/* initialize bch algorithm */ +	docg4->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); +	if (docg4->bch == NULL) +		return -1; + +	retval = nand_scan_tail(mtd); +	if (retval) +		return -1; + +	/* +	 * Scan for bad blocks and create bbt here, then add the factory-marked +	 * bad blocks to the bbt. +	 */ +	nand->scan_bbt(mtd); +	nand->options |= NAND_BBT_SCANNED; +	retval = read_factory_bbt(mtd); +	if (retval) +		return -1; + +	retval = nand_register(devnum); +	if (retval) +		return -1; + +	return 0; +} diff --git a/drivers/mtd/nand/docg4_spl.c b/drivers/mtd/nand/docg4_spl.c new file mode 100644 index 000000000..95e856c21 --- /dev/null +++ b/drivers/mtd/nand/docg4_spl.c @@ -0,0 +1,222 @@ +/* + * SPL driver for Diskonchip G4 nand flash + * + * Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + * + * + * This driver basically mimics the load functionality of a typical IPL (initial + * program loader) resident in the 2k NOR-like region of the docg4 that is + * mapped to the reset vector.  It allows the u-boot SPL to continue loading if + * the IPL loads a fixed number of flash blocks that is insufficient to contain + * the entire u-boot image.  In this case, a concatenated spl + u-boot image is + * written at the flash offset from which the IPL loads an image, and when the + * IPL jumps to the SPL, the SPL resumes loading where the IPL left off.  See + * the palmtreo680 for an example. + * + * This driver assumes that the data was written to the flash using the device's + * "reliable" mode, and also assumes that each 512 byte page is stored + * redundantly in the subsequent page.  This storage format is likely to be used + * by all boards that boot from the docg4.  The format compensates for the lack + * of ecc in the IPL. + * + * Reliable mode reduces the capacity of a block by half, and the redundant + * pages reduce it by half again.  As a result, the normal 256k capacity of a + * block is reduced to 64k for the purposes of the IPL/SPL. + */ + +#include <asm/io.h> +#include <linux/mtd/docg4.h> + +/* forward declarations */ +static inline void write_nop(void __iomem *docptr); +static int poll_status(void __iomem *docptr); +static void write_addr(void __iomem *docptr, uint32_t docg4_addr); +static void address_sequence(unsigned int g4_page, unsigned int g4_index, +			     void __iomem *docptr); +static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr); + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ +	void *load_addr = dst; +	uint32_t flash_offset = offs; +	const unsigned int block_count = +		(size + DOCG4_BLOCK_CAPACITY_SPL - 1) +		/ DOCG4_BLOCK_CAPACITY_SPL; +	int i; + +	for (i = 0; i < block_count; i++) { +		int ret = docg4_load_block_reliable(flash_offset, load_addr); +		if (ret) +			return ret; +		load_addr += DOCG4_BLOCK_CAPACITY_SPL; +		flash_offset += DOCG4_BLOCK_SIZE; +	} +	return 0; +} + +static inline void write_nop(void __iomem *docptr) +{ +	writew(0, docptr + DOC_NOP); +} + +static int poll_status(void __iomem *docptr) +{ +	/* +	 * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL +	 * register.  Operations known to take a long time (e.g., block erase) +	 * should sleep for a while before calling this. +	 */ + +	uint8_t flash_status; + +	/* hardware quirk requires reading twice initially */ +	flash_status = readb(docptr + DOC_FLASHCONTROL); + +	do { +		flash_status = readb(docptr + DOC_FLASHCONTROL); +	} while (!(flash_status & DOC_CTRL_FLASHREADY)); + +	return 0; +} + +static void write_addr(void __iomem *docptr, uint32_t docg4_addr) +{ +	/* write the four address bytes packed in docg4_addr to the device */ + +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +	docg4_addr >>= 8; +	writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static void address_sequence(unsigned int g4_page, unsigned int g4_index, +			     void __iomem *docptr) +{ +	writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); +	writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_addr(docptr, ((uint32_t)g4_page << 16) | g4_index); +	write_nop(docptr); +} + +static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr) +{ +	void __iomem *docptr = (void *)CONFIG_SYS_NAND_BASE; +	unsigned int g4_page = flash_offset >> 11; /* 2k page */ +	const unsigned int last_g4_page = g4_page + 0x80; /* last in block */ +	int g4_index = 0; +	uint16_t flash_status; +	uint16_t *buf; +	uint16_t discard, magic_high, magic_low; + +	/* flash_offset must be aligned to the start of a block */ +	if (flash_offset & 0x3ffff) +		return -1; + +	writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); +	writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); +	poll_status(docptr); +	write_nop(docptr); +	writew(0x45, docptr + DOC_FLASHSEQUENCE); +	writew(0xa3, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	writew(0x22, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); + +	/* read 1st 4 oob bytes of first subpage of block */ +	address_sequence(g4_page, 0x0100, docptr); /* index at oob */ +	write_nop(docptr); +	flash_status = readw(docptr + DOC_FLASHCONTROL); +	flash_status = readw(docptr + DOC_FLASHCONTROL); +	if (flash_status & 0x06) /* sequence or protection errors */ +		return -1; +	writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); +	write_nop(docptr); +	write_nop(docptr); +	poll_status(docptr); +	writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); +	write_nop(docptr); + +	/* +	 * Here we read the first four oob bytes of the first page of the block. +	 * The IPL on the palmtreo680 requires that this contain a 32 bit magic +	 * number, or the load aborts.  We'll ignore it. +	 */ +	discard = readw(docptr + 0x103c); /* hw quirk; 1st read discarded */ +	magic_low = readw(docptr + 0x103c); +	magic_high = readw(docptr + DOCG4_MYSTERY_REG); +	writew(0, docptr + DOC_DATAEND); +	write_nop(docptr); +	write_nop(docptr); + +	/* load contents of block to memory */ +	buf = (uint16_t *)dest_addr; +	do { +		int i; + +		address_sequence(g4_page, g4_index, docptr); +		writew(DOCG4_CMD_READ2, +		       docptr + DOC_FLASHCOMMAND); +		write_nop(docptr); +		write_nop(docptr); +		poll_status(docptr); +		writew(DOC_ECCCONF0_READ_MODE | +		       DOC_ECCCONF0_ECC_ENABLE | +		       DOCG4_BCH_SIZE, +		       docptr + DOC_ECCCONF0); +		write_nop(docptr); +		write_nop(docptr); +		write_nop(docptr); +		write_nop(docptr); +		write_nop(docptr); + +		/* read the 512 bytes of page data, 2 bytes at a time */ +		discard = readw(docptr + 0x103c); +		for (i = 0; i < 256; i++) +			*buf++ = readw(docptr + 0x103c); + +		/* read oob, but discard it */ +		for (i = 0; i < 7; i++) +			discard = readw(docptr + 0x103c); +		discard = readw(docptr + DOCG4_OOB_6_7); +		discard = readw(docptr + DOCG4_OOB_6_7); + +		writew(0, docptr + DOC_DATAEND); +		write_nop(docptr); +		write_nop(docptr); + +		if (!(g4_index & 0x100)) { +			/* not redundant subpage read; check for ecc error */ +			write_nop(docptr); +			flash_status = readw(docptr + DOC_ECCCONF1); +			flash_status = readw(docptr + DOC_ECCCONF1); +			if (flash_status & 0x80) { /* ecc error */ +				g4_index += 0x108; /* read redundant subpage */ +				buf -= 256;        /* back up ram ptr */ +				continue; +			} else                       /* no ecc error */ +				g4_index += 0x210; /* skip redundant subpage */ +		} else  /* redundant page was just read; skip ecc error check */ +			g4_index += 0x108; + +		if (g4_index == 0x420) { /* finished with 2k page */ +			g4_index = 0; +			g4_page += 2; /* odd-numbered 2k pages skipped */ +		} + +	} while (g4_page != last_g4_page); /* while still on same block */ + +	return 0; +} diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c index edc589e5b..ba725e9f1 100644 --- a/drivers/mtd/nand/mxc_nand_spl.c +++ b/drivers/mtd/nand/mxc_nand_spl.c @@ -290,7 +290,7 @@ static int is_badblock(int pagenumber)  	return 0;  } -static int nand_load(unsigned int from, unsigned int size, unsigned char *buf) +int nand_spl_load_image(uint32_t from, unsigned int size, void *buf)  {  	int i;  	unsigned int page; @@ -303,6 +303,7 @@ static int nand_load(unsigned int from, unsigned int size, unsigned char *buf)  	page = from / CONFIG_SYS_NAND_PAGE_SIZE;  	i = 0; +	size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE);  	while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) {  		if (nfc_read_page(page, buf) < 0)  			return -1; @@ -332,6 +333,7 @@ static int nand_load(unsigned int from, unsigned int size, unsigned char *buf)  	return 0;  } +#ifndef CONFIG_SPL_FRAMEWORK  /*   * The main entry for NAND booting. It's necessary that SDRAM is already   * configured and available since this code loads the main U-Boot image @@ -345,8 +347,9 @@ void nand_boot(void)  	 * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must  	 * be aligned to full pages  	 */ -	if (!nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, -		       (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { +	if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, +			CONFIG_SYS_NAND_U_BOOT_SIZE, +			(uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) {  		/* Copy from NAND successful, start U-boot */  		uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;  		uboot(); @@ -355,3 +358,7 @@ void nand_boot(void)  		hang();  	}  } +#endif + +void nand_init(void) {} +void nand_deselect(void) {} diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 7ba48c120..866cabd27 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -34,12 +34,19 @@  #include <asm/io.h>  #include <asm/arch/clock.h>  #include <asm/arch/imx-regs.h> +#include <asm/imx-common/regs-bch.h> +#include <asm/imx-common/regs-gpmi.h>  #include <asm/arch/sys_proto.h> -#include <asm/arch/dma.h> +#include <asm/imx-common/dma.h>  #define	MXS_NAND_DMA_DESCRIPTOR_COUNT		4  #define	MXS_NAND_CHUNK_DATA_CHUNK_SIZE		512 +#if defined(CONFIG_MX6) +#define	MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT	2 +#else +#define	MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT	0 +#endif  #define	MXS_NAND_METADATA_SIZE			10  #define	MXS_NAND_COMMAND_BUFFER_SIZE		32 @@ -983,14 +990,16 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)  	tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;  	tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)  		<< BCH_FLASHLAYOUT0_ECC0_OFFSET; -	tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; +	tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE +		>> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;  	writel(tmp, &bch_regs->hw_bch_flash0layout0);  	tmp = (mtd->writesize + mtd->oobsize)  		<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;  	tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)  		<< BCH_FLASHLAYOUT1_ECCN_OFFSET; -	tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; +	tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE +		>> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;  	writel(tmp, &bch_regs->hw_bch_flash0layout1);  	/* Set *all* chip selects to use layout 0 */ diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index bc558c4c9..dad30b54c 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -94,7 +94,7 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.idcode2 = 0x4d01,  		.pages_per_sector = 256,  		.nr_sectors = 256, -		.name = "S25FL129P_64K", +		.name = "S25FL129P_64K/S25FL128S",  	},  	{  		.idcode1 = 0x0219, diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 7a3685019..379b679d2 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -109,7 +109,13 @@ struct cpsw_slave_regs {  	u32	flow_thresh;  	u32	port_vlan;  	u32	tx_pri_map; +#ifdef CONFIG_AM33XX  	u32	gap_thresh; +#elif defined(CONFIG_TI814X) +	u32	ts_ctl; +	u32	ts_seq_ltype; +	u32	ts_vlan; +#endif  	u32	sa_lo;  	u32	sa_hi;  }; diff --git a/drivers/net/fm/b4860.c b/drivers/net/fm/b4860.c index 8cde7afc1..3b5defefa 100644 --- a/drivers/net/fm/b4860.c +++ b/drivers/net/fm/b4860.c @@ -55,8 +55,10 @@ phy_interface_t fman_port_enet_if(enum fm_port port)  	if (is_device_disabled(port))  		return PHY_INTERFACE_MODE_NONE; -	if ((port == FM1_10GEC1 || port == FM1_10GEC2) -			&& (is_serdes_configured(XAUI_FM1))) +	/*B4860 has two 10Gig Mac*/ +	if ((port == FM1_10GEC1 || port == FM1_10GEC2)	&& +	    ((is_serdes_configured(XAUI_FM1_MAC9))	|| +	    (is_serdes_configured(XAUI_FM1_MAC10))))  		return PHY_INTERFACE_MODE_XGMII;  	/* Fix me need to handle RGMII here first */ diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 5e90d7098..af5f4b848 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_PHYLIB_10G) += generic_10g.o  COBJS-$(CONFIG_PHY_ATHEROS) += atheros.o  COBJS-$(CONFIG_PHY_BROADCOM) += broadcom.o  COBJS-$(CONFIG_PHY_DAVICOM) += davicom.o +COBJS-$(CONFIG_PHY_ET1011C) += et1011c.o  COBJS-$(CONFIG_PHY_LXT) += lxt.o  COBJS-$(CONFIG_PHY_MARVELL) += marvell.o  COBJS-$(CONFIG_PHY_MICREL) += micrel.o diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c new file mode 100644 index 000000000..5e22399af --- /dev/null +++ b/drivers/net/phy/et1011c.c @@ -0,0 +1,110 @@ +/* + * ET1011C PHY driver + * + * Derived from Linux kernel driver by Chaithrika U S + * Copyright (C) 2013, 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. + * + */ +#include <config.h> +#include <phy.h> + +#define ET1011C_CONFIG_REG		(0x16) +#define ET1011C_TX_FIFO_MASK		(0x3 << 12) +#define ET1011C_TX_FIFO_DEPTH_8		(0x0 << 12) +#define ET1011C_TX_FIFO_DEPTH_16	(0x1 << 12) +#define ET1011C_INTERFACE_MASK		(0x7 << 0) +#define ET1011C_GMII_INTERFACE		(0x2 << 0) +#define ET1011C_SYS_CLK_EN		(0x1 << 4) +#define ET1011C_TX_CLK_EN		(0x1 << 5) + +#define ET1011C_STATUS_REG		(0x1A) +#define ET1011C_DUPLEX_STATUS		(0x1 << 7) +#define ET1011C_SPEED_MASK		(0x3 << 8) +#define ET1011C_SPEED_1000		(0x2 << 8) +#define ET1011C_SPEED_100		(0x1 << 8) +#define ET1011C_SPEED_10		(0x0 << 8) + +static int et1011c_config(struct phy_device *phydev) +{ +	int ctl = 0; +	ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); +	if (ctl < 0) +		return ctl; +	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | +		 BMCR_ANENABLE); +	/* First clear the PHY */ +	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl | BMCR_RESET); + +	return genphy_config_aneg(phydev); +} + +static int et1011c_parse_status(struct phy_device *phydev) +{ +	int mii_reg; +	int speed; + +	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_STATUS_REG); + +	if (mii_reg & ET1011C_DUPLEX_STATUS) +		phydev->duplex = DUPLEX_FULL; +	else +		phydev->duplex = DUPLEX_HALF; + +	speed = mii_reg & ET1011C_SPEED_MASK; +	switch (speed) { +	case ET1011C_SPEED_1000: +		phydev->speed = SPEED_1000; +		mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG); +		mii_reg &= ~ET1011C_TX_FIFO_MASK; +		phy_write(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG, +			  mii_reg | +			  ET1011C_GMII_INTERFACE | +			  ET1011C_SYS_CLK_EN | +#ifdef CONFIG_PHY_ET1011C_TX_CLK_FIX +			  ET1011C_TX_CLK_EN | +#endif +			  ET1011C_TX_FIFO_DEPTH_16); +		break; +	case ET1011C_SPEED_100: +		phydev->speed = SPEED_100; +		break; +	case ET1011C_SPEED_10: +		phydev->speed = SPEED_10; +		break; +	} + +	return 0; +} + +static int et1011c_startup(struct phy_device *phydev) +{ +	genphy_update_link(phydev); +	et1011c_parse_status(phydev); +	return 0; +} + +static struct phy_driver et1011c_driver = { +	.name		= "ET1011C", +	.uid		= 0x0282f014, +	.mask		= 0xfffffff0, +	.features	= PHY_GBIT_FEATURES, +	.config		= &et1011c_config, +	.startup	= &et1011c_startup, +}; + +int phy_et1011c_init(void) +{ +	phy_register(&et1011c_driver); + +	return 0; +} diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 4b271989a..46801c791 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -465,6 +465,16 @@ static struct phy_driver M88E1149S_driver = {  	.shutdown = &genphy_shutdown,  }; +static struct phy_driver M88E1518_driver = { +	.name = "Marvell 88E1518", +	.uid = 0x1410dd1, +	.mask = 0xffffff0, +	.features = PHY_GBIT_FEATURES, +	.config = &m88e1111s_config, +	.startup = &m88e1011s_startup, +	.shutdown = &genphy_shutdown, +}; +  int phy_marvell_init(void)  {  	phy_register(&M88E1149S_driver); @@ -474,6 +484,7 @@ int phy_marvell_init(void)  	phy_register(&M88E1118R_driver);  	phy_register(&M88E1111S_driver);  	phy_register(&M88E1011S_driver); +	phy_register(&M88E1518_driver);  	return 0;  } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d0ed7666e..f8c548147 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -430,6 +430,9 @@ int phy_init(void)  #ifdef CONFIG_PHY_DAVICOM  	phy_davicom_init();  #endif +#ifdef CONFIG_PHY_ET1011C +	phy_et1011c_init(); +#endif  #ifdef CONFIG_PHY_LXT  	phy_lxt_init();  #endif diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c index 78447b711..84ce7362f 100644 --- a/drivers/net/phy/teranetics.c +++ b/drivers/net/phy/teranetics.c @@ -34,9 +34,21 @@ int tn2020_config(struct phy_device *phydev)  		unsigned short restart_an = (MDIO_AN_CTRL1_RESTART |  						MDIO_AN_CTRL1_ENABLE |  						MDIO_AN_CTRL1_XNP); +		u8 phy_hwversion; -		phy_write(phydev, 30, 93, 2); -		phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an); +		/* +		 * bit 15:12 of register 30.32 indicates PHY hardware +		 * version. It can be used to distinguish TN80xx from +		 * TN2020. TN2020 needs write 0x2 to 30.93, but TN80xx +		 * needs 0x1. +		 */ +		phy_hwversion = (phy_read(phydev, 30, 32) >> 12) & 0xf; +		if (phy_hwversion <= 3) { +			phy_write(phydev, 30, 93, 2); +			phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an); +		} else { +			phy_write(phydev, 30, 93, 1); +		}  	}  	return 0; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 359606569..eac9b6f45 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -33,6 +33,8 @@  #include <phy.h>  #include <miiphy.h>  #include <watchdog.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h>  #if !defined(CONFIG_PHYLIB)  # error XILINX_GEM_ETHERNET requires PHYLIB @@ -67,13 +69,14 @@  #define ZYNQ_GEM_NWCTRL_MDEN_MASK	0x00000010 /* Enable MDIO port */  #define ZYNQ_GEM_NWCTRL_STARTTX_MASK	0x00000200 /* Start tx (tx_go) */ -#define ZYNQ_GEM_NWCFG_SPEED		0x00000001 /* 100 Mbps operation */ -#define ZYNQ_GEM_NWCFG_FDEN		0x00000002 /* Full Duplex mode */ -#define ZYNQ_GEM_NWCFG_FSREM		0x00020000 /* FCS removal */ +#define ZYNQ_GEM_NWCFG_SPEED100		0x000000001 /* 100 Mbps operation */ +#define ZYNQ_GEM_NWCFG_SPEED1000	0x000000400 /* 1Gbps operation */ +#define ZYNQ_GEM_NWCFG_FDEN		0x000000002 /* Full Duplex mode */ +#define ZYNQ_GEM_NWCFG_FSREM		0x000020000 /* FCS removal */  #define ZYNQ_GEM_NWCFG_MDCCLKDIV	0x000080000 /* Div pclk by 32, 80MHz */ +#define ZYNQ_GEM_NWCFG_MDCCLKDIV2	0x0000c0000 /* Div pclk by 48, 120MHz */ -#define ZYNQ_GEM_NWCFG_INIT		(ZYNQ_GEM_NWCFG_SPEED | \ -					ZYNQ_GEM_NWCFG_FDEN | \ +#define ZYNQ_GEM_NWCFG_INIT		(ZYNQ_GEM_NWCFG_FDEN | \  					ZYNQ_GEM_NWCFG_FSREM | \  					ZYNQ_GEM_NWCFG_MDCCLKDIV) @@ -92,6 +95,17 @@  					ZYNQ_GEM_DMACR_TXSIZE | \  					ZYNQ_GEM_DMACR_RXBUF) +/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG  1 + +/* Mask used to verify certain PHY features (or register contents) + * in the register above: + *  0x1000: 10Mbps full duplex support + *  0x0800: 10Mbps half duplex support + *  0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x1808 +  /* Device registers */  struct zynq_gem_regs {  	u32 nwctrl; /* Network Control reg */ @@ -134,6 +148,8 @@ struct zynq_gem_priv {  	u32 rxbd_current;  	u32 rx_first_buf;  	int phyaddr; +	u32 emio; +	int init;  	struct phy_device *phydev;  	struct mii_dev *bus;  }; @@ -196,6 +212,44 @@ static u32 phywrite(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 data)  				ZYNQ_GEM_PHYMNTNC_OP_W_MASK, &data);  } +static void phy_detection(struct eth_device *dev) +{ +	int i; +	u16 phyreg; +	struct zynq_gem_priv *priv = dev->priv; + +	if (priv->phyaddr != -1) { +		phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg); +		if ((phyreg != 0xFFFF) && +		    ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { +			/* Found a valid PHY address */ +			debug("Default phy address %d is valid\n", +			      priv->phyaddr); +			return; +		} else { +			debug("PHY address is not setup correctly %d\n", +			      priv->phyaddr); +			priv->phyaddr = -1; +		} +	} + +	debug("detecting phy address\n"); +	if (priv->phyaddr == -1) { +		/* detect the PHY address */ +		for (i = 31; i >= 0; i--) { +			phyread(dev, i, PHY_DETECT_REG, &phyreg); +			if ((phyreg != 0xFFFF) && +			    ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { +				/* Found a valid PHY address */ +				priv->phyaddr = i; +				debug("Found valid phy address, %d\n", i); +				return; +			} +		} +	} +	printf("PHY is not detected\n"); +} +  static int zynq_gem_setup_mac(struct eth_device *dev)  {  	u32 i, macaddrlow, macaddrhigh; @@ -226,7 +280,7 @@ static int zynq_gem_setup_mac(struct eth_device *dev)  static int zynq_gem_init(struct eth_device *dev, bd_t * bis)  { -	u32 i; +	u32 i, rclk, clk = 0;  	struct phy_device *phydev;  	const u32 stat_size = (sizeof(struct zynq_gem_regs) -  				offsetof(struct zynq_gem_regs, stat)) / 4; @@ -239,59 +293,92 @@ static int zynq_gem_init(struct eth_device *dev, bd_t * bis)  			SUPPORTED_1000baseT_Half |  			SUPPORTED_1000baseT_Full; -	/* Disable all interrupts */ -	writel(0xFFFFFFFF, ®s->idr); +	if (!priv->init) { +		/* Disable all interrupts */ +		writel(0xFFFFFFFF, ®s->idr); -	/* Disable the receiver & transmitter */ -	writel(0, ®s->nwctrl); -	writel(0, ®s->txsr); -	writel(0, ®s->rxsr); -	writel(0, ®s->phymntnc); +		/* Disable the receiver & transmitter */ +		writel(0, ®s->nwctrl); +		writel(0, ®s->txsr); +		writel(0, ®s->rxsr); +		writel(0, ®s->phymntnc); -	/* Clear the Hash registers for the mac address pointed by AddressPtr */ -	writel(0x0, ®s->hashl); -	/* Write bits [63:32] in TOP */ -	writel(0x0, ®s->hashh); +		/* Clear the Hash registers for the mac address +		 * pointed by AddressPtr +		 */ +		writel(0x0, ®s->hashl); +		/* Write bits [63:32] in TOP */ +		writel(0x0, ®s->hashh); -	/* Clear all counters */ -	for (i = 0; i <= stat_size; i++) -		readl(®s->stat[i]); +		/* Clear all counters */ +		for (i = 0; i <= stat_size; i++) +			readl(®s->stat[i]); -	/* Setup RxBD space */ -	memset(&(priv->rx_bd), 0, sizeof(priv->rx_bd)); -	/* Create the RxBD ring */ -	memset(&(priv->rxbuffers), 0, sizeof(priv->rxbuffers)); +		/* Setup RxBD space */ +		memset(&(priv->rx_bd), 0, sizeof(priv->rx_bd)); +		/* Create the RxBD ring */ +		memset(&(priv->rxbuffers), 0, sizeof(priv->rxbuffers)); -	for (i = 0; i < RX_BUF; i++) { -		priv->rx_bd[i].status = 0xF0000000; -		priv->rx_bd[i].addr = (u32)((char *) &(priv->rxbuffers) + +		for (i = 0; i < RX_BUF; i++) { +			priv->rx_bd[i].status = 0xF0000000; +			priv->rx_bd[i].addr = +					(u32)((char *)&(priv->rxbuffers) +  							(i * PKTSIZE_ALIGN)); -	} -	/* WRAP bit to last BD */ -	priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK; -	/* Write RxBDs to IP */ -	writel((u32) &(priv->rx_bd), ®s->rxqbase); +		} +		/* WRAP bit to last BD */ +		priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK; +		/* Write RxBDs to IP */ +		writel((u32)&(priv->rx_bd), ®s->rxqbase); -	/* MAC Setup */ -	/* Setup Network Configuration register */ -	writel(ZYNQ_GEM_NWCFG_INIT, ®s->nwcfg); +		/* Setup for DMA Configuration register */ +		writel(ZYNQ_GEM_DMACR_INIT, ®s->dmacr); -	/* Setup for DMA Configuration register */ -	writel(ZYNQ_GEM_DMACR_INIT, ®s->dmacr); +		/* Setup for Network Control register, MDIO, Rx and Tx enable */ +		setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_MDEN_MASK); -	/* Setup for Network Control register, MDIO, Rx and Tx enable */ -	setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_MDEN_MASK | -			ZYNQ_GEM_NWCTRL_RXEN_MASK | ZYNQ_GEM_NWCTRL_TXEN_MASK); +		priv->init++; +	} + +	phy_detection(dev);  	/* interface - look at tsec */  	phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0); -	phydev->supported &= supported; +	phydev->supported = supported | ADVERTISED_Pause | +			    ADVERTISED_Asym_Pause;  	phydev->advertising = phydev->supported;  	priv->phydev = phydev;  	phy_config(phydev);  	phy_startup(phydev); +	switch (phydev->speed) { +	case SPEED_1000: +		writel(ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED1000, +		       ®s->nwcfg); +		rclk = (0 << 4) | (1 << 0); +		clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0); +		break; +	case SPEED_100: +		clrsetbits_le32(®s->nwcfg, ZYNQ_GEM_NWCFG_SPEED1000, +				ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED100); +		rclk = 1 << 0; +		clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); +		break; +	case SPEED_10: +		rclk = 1 << 0; +		/* FIXME untested */ +		clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); +		break; +	} + +	/* Change the rclk and clk only not using EMIO interface */ +	if (!priv->emio) +		zynq_slcr_gem_clk_setup(dev->iobase != +					ZYNQ_GEM_BASEADDR0, rclk, clk); + +	setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | +					ZYNQ_GEM_NWCTRL_TXEN_MASK); +  	return 0;  } @@ -307,11 +394,10 @@ static int zynq_gem_send(struct eth_device *dev, void *ptr, int len)  	writel((u32)&(priv->tx_bd), ®s->txqbase);  	/* Setup Tx BD */ -	memset((void *) &(priv->tx_bd), 0, sizeof(struct emac_bd)); +	memset((void *)&(priv->tx_bd), 0, sizeof(struct emac_bd));  	priv->tx_bd.addr = (u32)ptr; -	priv->tx_bd.status = len | ZYNQ_GEM_TXBUF_LAST_MASK | -						ZYNQ_GEM_TXBUF_WRAP_MASK; +	priv->tx_bd.status = len | ZYNQ_GEM_TXBUF_LAST_MASK;  	/* Start transmit */  	setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_STARTTX_MASK); @@ -364,19 +450,17 @@ static int zynq_gem_recv(struct eth_device *dev)  		if ((++priv->rxbd_current) >= RX_BUF)  			priv->rxbd_current = 0; - -		return frame_len;  	} -	return 0; +	return frame_len;  }  static void zynq_gem_halt(struct eth_device *dev)  {  	struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; -	/* Disable the receiver & transmitter */ -	writel(0, ®s->nwctrl); +	clrsetbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | +						ZYNQ_GEM_NWCTRL_TXEN_MASK, 0);  }  static int zynq_gem_miiphyread(const char *devname, uchar addr, @@ -399,7 +483,7 @@ static int zynq_gem_miiphy_write(const char *devname, uchar addr,  	return phywrite(dev, addr, reg, val);  } -int zynq_gem_initialize(bd_t *bis, int base_addr) +int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio)  {  	struct eth_device *dev;  	struct zynq_gem_priv *priv; @@ -415,11 +499,8 @@ int zynq_gem_initialize(bd_t *bis, int base_addr)  	}  	priv = dev->priv; -#ifdef CONFIG_PHY_ADDR -	priv->phyaddr = CONFIG_PHY_ADDR; -#else -	priv->phyaddr = -1; -#endif +	priv->phyaddr = phy_addr; +	priv->emio = emio;  	sprintf(dev->name, "Gem.%x", base_addr); diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 1dac16a9f..a9c42370f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -30,7 +30,7 @@ COBJS-$(CONFIG_FTPMU010_POWER)	+= ftpmu010.o  COBJS-$(CONFIG_TPS6586X_POWER)	+= tps6586x.o  COBJS-$(CONFIG_TWL4030_POWER)	+= twl4030.o  COBJS-$(CONFIG_TWL6030_POWER)	+= twl6030.o -COBJS-$(CONFIG_TWL6035_POWER)	+= twl6035.o +COBJS-$(CONFIG_PALMAS_POWER)	+= palmas.o  COBJS-$(CONFIG_POWER) += power_core.o  COBJS-$(CONFIG_DIALOG_POWER) += power_dialog.o diff --git a/drivers/power/twl6035.c b/drivers/power/palmas.c index d3de698cd..09c832d8b 100644 --- a/drivers/power/twl6035.c +++ b/drivers/power/palmas.c @@ -1,5 +1,5 @@  /* - * (C) Copyright 2012 + * (C) Copyright 2012-2013   * Texas Instruments, <www.ti.com>   *   * See file CREDITS for list of people who contributed to this @@ -21,43 +21,21 @@   * MA 02111-1307 USA   */  #include <config.h> -#include <twl6035.h> +#include <palmas.h> -/* Functions to read and write from TWL6030 */ -int twl6035_i2c_write_u8(u8 chip_no, u8 val, u8 reg) -{ -	return i2c_write(chip_no, reg, 1, &val, 1); -} - -int twl6035_i2c_read_u8(u8 chip_no, u8 *val, u8 reg) -{ -	return i2c_read(chip_no, reg, 1, val, 1); -} - -/* To align with i2c mw/mr address, reg, val command syntax */ -static inline int palmas_write_u8(u8 chip_no, u8 reg, u8 val) -{ -	return i2c_write(chip_no, reg, 1, &val, 1); -} - -static inline int palmas_read_u8(u8 chip_no, u8 reg, u8 *val) -{ -	return i2c_read(chip_no, reg, 1, val, 1); -} - -void twl6035_init_settings(void) +void palmas_init_settings(void)  {  	return;  } -int twl6035_mmc1_poweron_ldo(void) +int palmas_mmc1_poweron_ldo(void)  {  	u8 val = 0;  	/* set LDO9 TWL6035 to 3V */  	val = 0x2b; /* (3 -.9)*28 +1 */ -	if (palmas_write_u8(0x48, LDO9_VOLTAGE, val)) { +	if (palmas_i2c_write_u8(0x48, LDO9_VOLTAGE, val)) {  		printf("twl6035: could not set LDO9 voltage.\n");  		return 1;  	} @@ -65,7 +43,7 @@ int twl6035_mmc1_poweron_ldo(void)  	/* TURN ON LDO9 */  	val = LDO_ON | LDO_MODE_SLEEP | LDO_MODE_ACTIVE; -	if (palmas_write_u8(0x48, LDO9_CTRL, val)) { +	if (palmas_i2c_write_u8(0x48, LDO9_CTRL, val)) {  		printf("twl6035: could not turn on LDO9.\n");  		return 1;  	} diff --git a/drivers/power/twl4030.c b/drivers/power/twl4030.c index e7d5f132b..6610f787d 100644 --- a/drivers/power/twl4030.c +++ b/drivers/power/twl4030.c @@ -45,14 +45,14 @@  void twl4030_power_reset_init(void)  {  	u8 val = 0; -	if (twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, &val, -				TWL4030_PM_MASTER_P1_SW_EVENTS)) { +	if (twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, +				TWL4030_PM_MASTER_P1_SW_EVENTS, &val)) {  		printf("Error:TWL4030: failed to read the power register\n");  		printf("Could not initialize hardware reset\n");  	} else {  		val |= TWL4030_PM_MASTER_SW_EVENTS_STOPON_PWRON; -		if (twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, val, -					 TWL4030_PM_MASTER_P1_SW_EVENTS)) { +		if (twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, +					 TWL4030_PM_MASTER_P1_SW_EVENTS, val)) {  			printf("Error:TWL4030: failed to write the power register\n");  			printf("Could not initialize hardware reset\n");  		} @@ -68,8 +68,8 @@ void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val,  	int ret;  	/* Select the Voltage */ -	ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_val, -				vsel_reg); +	ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_reg, +				   vsel_val);  	if (ret != 0) {  		printf("Could not write vsel to reg %02x (%d)\n",  			vsel_reg, ret); @@ -77,8 +77,8 @@ void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val,  	}  	/* Select the Device Group (enable the supply if dev_grp_sel != 0) */ -	ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp_sel, -				dev_grp); +	ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp, +				   dev_grp_sel);  	if (ret != 0)  		printf("Could not write grp_sel to reg %02x (%d)\n",  			dev_grp, ret); diff --git a/drivers/power/twl6030.c b/drivers/power/twl6030.c index c5a0038ca..d421e6005 100644 --- a/drivers/power/twl6030.c +++ b/drivers/power/twl6030.c @@ -25,30 +25,19 @@  #include <twl6030.h> -/* Functions to read and write from TWL6030 */ -static inline int twl6030_i2c_write_u8(u8 chip_no, u8 val, u8 reg) -{ -	return i2c_write(chip_no, reg, 1, &val, 1); -} - -static inline int twl6030_i2c_read_u8(u8 chip_no, u8 *val, u8 reg) -{ -	return i2c_read(chip_no, reg, 1, val, 1); -} -  static int twl6030_gpadc_read_channel(u8 channel_no)  {  	u8 lsb = 0;  	u8 msb = 0;  	int ret = 0; -	ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &lsb, -				GPCH0_LSB + channel_no * 2); +	ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, +				  GPCH0_LSB + channel_no * 2, &lsb);  	if (ret)  		return ret; -	ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &msb, -				GPCH0_MSB + channel_no * 2); +	ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, +				  GPCH0_MSB + channel_no * 2, &msb);  	if (ret)  		return ret; @@ -60,7 +49,7 @@ static int twl6030_gpadc_sw2_trigger(void)  	u8 val;  	int ret = 0; -	ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, CTRL_P2_SP2, CTRL_P2); +	ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, CTRL_P2, CTRL_P2_SP2);  	if (ret)  		return ret; @@ -68,7 +57,7 @@ static int twl6030_gpadc_sw2_trigger(void)  	val =  CTRL_P2_BUSY;  	while (!((val & CTRL_P2_EOCP2) && (!(val & CTRL_P2_BUSY)))) { -		ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &val, CTRL_P2); +		ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, CTRL_P2, &val);  		if (ret)  			return ret;  		udelay(1000); @@ -79,29 +68,29 @@ static int twl6030_gpadc_sw2_trigger(void)  void twl6030_stop_usb_charging(void)  { -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, 0, CONTROLLER_CTRL1); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CONTROLLER_CTRL1, 0);  	return;  }  void twl6030_start_usb_charging(void)  { -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_VICHRG_1500, -							CHARGERUSB_VICHRG); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CIN_LIMIT_NONE, -							CHARGERUSB_CINLIMIT); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, MBAT_TEMP, -							CONTROLLER_INT_MASK); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, MASK_MCHARGERUSB_THMREG, -							CHARGERUSB_INT_MASK); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_VOREG_4P0, -							CHARGERUSB_VOREG); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL2_VITERM_400, -							CHARGERUSB_CTRL2); -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, TERM, CHARGERUSB_CTRL1); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CHARGERUSB_VICHRG, CHARGERUSB_VICHRG_1500); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CHARGERUSB_CINLIMIT, CHARGERUSB_CIN_LIMIT_NONE); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CONTROLLER_INT_MASK, MBAT_TEMP); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CHARGERUSB_INT_MASK, MASK_MCHARGERUSB_THMREG); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CHARGERUSB_VOREG, CHARGERUSB_VOREG_4P0); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CHARGERUSB_CTRL2, CHARGERUSB_CTRL2_VITERM_400); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL1, TERM);  	/* Enable USB charging */ -	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CONTROLLER_CTRL1_EN_CHARGER, -							CONTROLLER_CTRL1); +	twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, +			     CONTROLLER_CTRL1, CONTROLLER_CTRL1_EN_CHARGER);  	return;  } @@ -111,8 +100,8 @@ int twl6030_get_battery_current(void)  	u8 msb = 0;  	u8 lsb = 0; -	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &msb, FG_REG_11); -	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &lsb, FG_REG_10); +	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_11, &msb); +	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_10, &lsb);  	battery_current = ((msb << 8) | lsb);  	/* convert 10 bit signed number to 16 bit signed number */ @@ -156,10 +145,10 @@ void twl6030_init_battery_charging(void)  	int ret = 0;  	/* Enable VBAT measurement */ -	twl6030_i2c_write_u8(TWL6030_CHIP_PM, VBAT_MEAS, MISC1); +	twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC1, VBAT_MEAS);  	/* Enable GPADC module */ -	ret = twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, FGS | GPADCS, TOGGLE1); +	ret = twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, TOGGLE1, FGS | GPADCS);  	if (ret) {  		printf("Failed to enable GPADC\n");  		return; @@ -173,7 +162,7 @@ void twl6030_init_battery_charging(void)  		printf("Main battery voltage too low!\n");  	/* Check for the presence of USB charger */ -	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &stat1, CONTROLLER_STAT1); +	twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, CONTROLLER_STAT1, &stat1);  	/* check for battery presence indirectly via Fuel gauge */  	if ((stat1 & VBUS_DET) && (battery_volt < 3300)) @@ -185,8 +174,8 @@ void twl6030_init_battery_charging(void)  void twl6030_power_mmc_init()  {  	/* set voltage to 3.0 and turnon for APP */ -	twl6030_i2c_write_u8(TWL6030_CHIP_PM, 0x15, VMMC_CFG_VOLTATE); -	twl6030_i2c_write_u8(TWL6030_CHIP_PM, 0x21, VMMC_CFG_STATE); +	twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_VOLTATE, 0x15); +	twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_STATE, 0x21);  }  void twl6030_usb_device_settings() @@ -194,12 +183,12 @@ void twl6030_usb_device_settings()  	u8 data = 0;  	/* Select APP Group and set state to ON */ -	twl6030_i2c_write_u8(TWL6030_CHIP_PM, 0x21, VUSB_CFG_STATE); +	twl6030_i2c_write_u8(TWL6030_CHIP_PM, VUSB_CFG_STATE, 0x21); -	twl6030_i2c_read_u8(TWL6030_CHIP_PM, &data, MISC2); +	twl6030_i2c_read_u8(TWL6030_CHIP_PM, MISC2, &data);  	data |= 0x10;  	/* Select the input supply for VBUS regulator */ -	twl6030_i2c_write_u8(TWL6030_CHIP_PM, data, MISC2); +	twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC2, data);  }  #endif diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index f4b1bad22..52594e352 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -30,6 +30,15 @@  #include "atmel_spi.h" +static int spi_has_wdrbt(struct atmel_spi_slave *slave) +{ +	unsigned int ver; + +	ver = spi_readl(slave, VERSION); + +	return (ATMEL_SPI_VERSION_REV(ver) >= 0x210); +} +  void spi_init()  { @@ -90,10 +99,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	as->regs = regs;  	as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS -#if defined(CONFIG_AT91SAM9X5) || defined(CONFIG_AT91SAM9M10G45) -			| ATMEL_SPI_MR_WDRBT -#endif  			| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); +	if (spi_has_wdrbt(as)) +		as->mr |= ATMEL_SPI_MR_WDRBT; +  	spi_writel(as, CSR(cs), csrx);  	return &as->slave; diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h index 057de9ade..d2409454f 100644 --- a/drivers/spi/atmel_spi.h +++ b/drivers/spi/atmel_spi.h @@ -64,7 +64,7 @@  #define ATMEL_SPI_CSRx_DLYBCT(x)	((x) << 24)  /* Bits in VERSION */ -#define ATMEL_SPI_VERSION_REV(x)	((x) << 0) +#define ATMEL_SPI_VERSION_REV(x)	((x) & 0xfff)  #define ATMEL_SPI_VERSION_MFN(x)	((x) << 16)  /* Constants for CSRx:BITS */ diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index aa999f9a9..db98a136e 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -31,7 +31,7 @@  #include <asm/arch/clock.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/sys_proto.h> -#include <asm/arch/dma.h> +#include <asm/imx-common/dma.h>  #define	MXS_SPI_MAX_TIMEOUT	1000000  #define	MXS_SPI_PORT_OFFSET	0x2000 diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 4c0008174..71cc0f2a0 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -610,7 +610,9 @@ void udc_connect(void)  #ifdef CONFIG_USB_DEV_PULLUP_GPIO  	/* Turn on the USB connection by enabling the pullup resistor */ -	set_GPIO_mode(CONFIG_USB_DEV_PULLUP_GPIO | GPIO_OUT); +	writel(readl(GPDR(CONFIG_USB_DEV_PULLUP_GPIO)) +		     | GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), +	       GPDR(CONFIG_USB_DEV_PULLUP_GPIO));  	writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO));  #else  	/* Host port 2 transceiver D+ pull up enable */ diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index adbed5c90..f43c38da6 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -21,8 +21,6 @@  #include <asm/io.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/clock.h> -#include <asm/arch/mx5x_pins.h> -#include <asm/arch/iomux.h>  #include "ehci.h" @@ -87,77 +85,6 @@  /* USB_CTRL_1 */  #define MXC_USB_CTRL_UH1_EXT_CLK_EN	(1 << 25) -/* USB pin configuration */ -#define USB_PAD_CONFIG	(PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ -			PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ -			PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) - -#ifdef CONFIG_MX51 -/* - * Configure the MX51 USB H1 IOMUX - */ -void setup_iomux_usb_h1(void) -{ -	mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG); - -	mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0); -	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG); -} - -/* - * Configure the MX51 USB H2 IOMUX - */ -void setup_iomux_usb_h2(void) -{ -	mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG); - -	mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG); -	mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2); -	mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG); -} -#endif -  int mxc_set_usbcontrol(int port, unsigned int flags)  {  	unsigned int v; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index aa5cf57ae..2060a3eb4 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -42,7 +42,7 @@ int usb_cpu_init(void)  	while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB)  		;  #elif defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \ -	defined(CONFIG_AT91SAM9X5) +	defined(CONFIG_AT91SAM9X5) || defined(CONFIG_SAMA5D3)  	/* Enable UPLL */  	writel(readl(&pmc->uckr) | AT91_PMC_UPLLEN | AT91_PMC_BIASEN,  		&pmc->uckr); @@ -54,7 +54,12 @@ int usb_cpu_init(void)  #endif  	/* Enable USB host clock. */ +#ifdef CONFIG_SAMA5D3 +	writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcer1); +#else  	writel(1 << ATMEL_ID_UHP, &pmc->pcer); +#endif +  #if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10)  	writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scer);  #else @@ -69,7 +74,12 @@ int usb_cpu_stop(void)  	at91_pmc_t *pmc	= (at91_pmc_t *)ATMEL_BASE_PMC;  	/* Disable USB host clock. */ +#ifdef CONFIG_SAMA5D3 +	writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcdr1); +#else  	writel(1 << ATMEL_ID_UHP, &pmc->pcdr); +#endif +  #if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10)  	writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scdr);  #else @@ -83,7 +93,7 @@ int usb_cpu_stop(void)  	while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != 0)  		;  #elif defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \ -	defined(CONFIG_AT91SAM9X5) +	defined(CONFIG_AT91SAM9X5) || defined(CONFIG_SAMA5D3)  	/* Disable UPLL */  	writel(readl(&pmc->uckr) & (~AT91_PMC_UPLLEN), &pmc->uckr);  	while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) diff --git a/drivers/usb/phy/twl4030.c b/drivers/usb/phy/twl4030.c index 54d2e615c..74f1dccbd 100644 --- a/drivers/usb/phy/twl4030.c +++ b/drivers/usb/phy/twl4030.c @@ -54,7 +54,7 @@ static int twl4030_usb_write(u8 address, u8 data)  {  	int ret; -	ret = twl4030_i2c_write_u8(TWL4030_CHIP_USB, data, address); +	ret = twl4030_i2c_write_u8(TWL4030_CHIP_USB, address, data);  	if (ret != 0)  		printf("TWL4030:USB:Write[0x%x] Error %d\n", address, ret); @@ -66,7 +66,7 @@ static int twl4030_usb_read(u8 address)  	u8 data;  	int ret; -	ret = twl4030_i2c_read_u8(TWL4030_CHIP_USB, &data, address); +	ret = twl4030_i2c_read_u8(TWL4030_CHIP_USB, address, &data);  	if (ret == 0)  		ret = data;  	else @@ -78,40 +78,40 @@ static int twl4030_usb_read(u8 address)  static void twl4030_usb_ldo_init(void)  {  	/* Enable writing to power configuration registers */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0xC0, -			     TWL4030_PM_MASTER_PROTECT_KEY); -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0x0C, -			     TWL4030_PM_MASTER_PROTECT_KEY); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, +			     TWL4030_PM_MASTER_PROTECT_KEY, 0xC0); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, +			     TWL4030_PM_MASTER_PROTECT_KEY, 0x0C);  	/* put VUSB3V1 LDO in active state */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00, -			     TWL4030_PM_RECEIVER_VUSB_DEDICATED2); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB_DEDICATED2, 0x00);  	/* input to VUSB3V1 LDO is from VBAT, not VBUS */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x14, -			     TWL4030_PM_RECEIVER_VUSB_DEDICATED1); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB_DEDICATED1, 0x14);  	/* turn on 3.1V regulator */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20, -			     TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP); -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00, -			     TWL4030_PM_RECEIVER_VUSB3V1_TYPE); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP, 0x20); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB3V1_TYPE, 0x00);  	/* turn on 1.5V regulator */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20, -			     TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP); -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00, -			     TWL4030_PM_RECEIVER_VUSB1V5_TYPE); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP, 0x20); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB1V5_TYPE, 0x00);  	/* turn on 1.8V regulator */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20, -			     TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP); -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00, -			     TWL4030_PM_RECEIVER_VUSB1V8_TYPE); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP, 0x20); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, +			     TWL4030_PM_RECEIVER_VUSB1V8_TYPE, 0x00);  	/* disable access to power configuration registers */ -	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0x00, -			     TWL4030_PM_MASTER_PROTECT_KEY); +	twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, +			     TWL4030_PM_MASTER_PROTECT_KEY, 0x00);  }  static void twl4030_phy_power(void) diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 53952ab07..68ff34bfd 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -49,6 +49,7 @@ COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o  COBJS-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o  COBJS-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o  COBJS-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +COBJS-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o  COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o  COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o  COBJS-$(CONFIG_VIDEO_SM501) += sm501.o diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 61e1058ad..0793f07f2 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -151,6 +151,10 @@  #endif  #endif +#ifdef CONFIG_VIDEO_MXS +#define VIDEO_FB_16BPP_WORD_SWAP +#endif +  /*   * Defines for the MB862xx driver   */ diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c new file mode 100644 index 000000000..461ff6e86 --- /dev/null +++ b/drivers/video/mxsfb.c @@ -0,0 +1,189 @@ +/* + * Freescale i.MX23/i.MX28 LCDIF driver + * + * Copyright (C) 2011-2013 Marek Vasut <marex@denx.de> + * + * 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 <video_fb.h> + +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/errno.h> +#include <asm/io.h> + +#include "videomodes.h" + +#define	PS2KHZ(ps)	(1000000000UL / (ps)) + +static GraphicDevice panel; + +/* + * DENX M28EVK: + * setenv videomode + * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066, + *       le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0 + */ + +static void mxs_lcd_init(GraphicDevice *panel, +			struct ctfb_res_modes *mode, int bpp) +{ +	struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; +	uint32_t word_len = 0, bus_width = 0; +	uint8_t valid_data = 0; + +	/* Kick in the LCDIF clock */ +	mxs_set_lcdclk(PS2KHZ(mode->pixclock)); + +	/* Restart the LCDIF block */ +	mxs_reset_block(®s->hw_lcdif_ctrl_reg); + +	switch (bpp) { +	case 24: +		word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; +		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT; +		valid_data = 0x7; +		break; +	case 18: +		word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; +		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT; +		valid_data = 0x7; +		break; +	case 16: +		word_len = LCDIF_CTRL_WORD_LENGTH_16BIT; +		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT; +		valid_data = 0xf; +		break; +	case 8: +		word_len = LCDIF_CTRL_WORD_LENGTH_8BIT; +		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT; +		valid_data = 0xf; +		break; +	} + +	writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE | +		LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER, +		®s->hw_lcdif_ctrl); + +	writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET, +		®s->hw_lcdif_ctrl1); +	writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres, +		®s->hw_lcdif_transfer_count); + +	writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | +		LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | +		LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | +		mode->vsync_len, ®s->hw_lcdif_vdctrl0); +	writel(mode->upper_margin + mode->lower_margin + +		mode->vsync_len + mode->yres, +		®s->hw_lcdif_vdctrl1); +	writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) | +		(mode->left_margin + mode->right_margin + +		mode->hsync_len + mode->xres), +		®s->hw_lcdif_vdctrl2); +	writel(((mode->left_margin + mode->hsync_len) << +		LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) | +		(mode->upper_margin + mode->vsync_len), +		®s->hw_lcdif_vdctrl3); +	writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres, +		®s->hw_lcdif_vdctrl4); + +	writel(panel->frameAdrs, ®s->hw_lcdif_cur_buf); +	writel(panel->frameAdrs, ®s->hw_lcdif_next_buf); + +	/* Flush FIFO first */ +	writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_set); + +	/* Sync signals ON */ +	setbits_le32(®s->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON); + +	/* FIFO cleared */ +	writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_clr); + +	/* RUN! */ +	writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); +} + +void *video_hw_init(void) +{ +	int bpp = -1; +	char *penv; +	void *fb; +	struct ctfb_res_modes mode; + +	puts("Video: "); + +	/* Suck display configuration from "videomode" variable */ +	penv = getenv("videomode"); +	if (!penv) { +		printf("MXSFB: 'videomode' variable not set!"); +		return NULL; +	} + +	bpp = video_get_params(&mode, penv); + +	/* fill in Graphic device struct */ +	sprintf(panel.modeIdent, "%dx%dx%d", +			mode.xres, mode.yres, bpp); + +	panel.winSizeX = mode.xres; +	panel.winSizeY = mode.yres; +	panel.plnSizeX = mode.xres; +	panel.plnSizeY = mode.yres; + +	switch (bpp) { +	case 24: +	case 18: +		panel.gdfBytesPP = 4; +		panel.gdfIndex = GDF_32BIT_X888RGB; +		break; +	case 16: +		panel.gdfBytesPP = 2; +		panel.gdfIndex = GDF_16BIT_565RGB; +		break; +	case 8: +		panel.gdfBytesPP = 1; +		panel.gdfIndex = GDF__8BIT_INDEX; +		break; +	default: +		printf("MXSFB: Invalid BPP specified! (bpp = %i)\n", bpp); +		return NULL; +	} + +	panel.memSize = mode.xres * mode.yres * panel.gdfBytesPP; + +	/* Allocate framebuffer */ +	fb = malloc(panel.memSize); +	if (!fb) { +		printf("MXSFB: Error allocating framebuffer!\n"); +		return NULL; +	} + +	/* Wipe framebuffer */ +	memset(fb, 0, panel.memSize); + +	panel.frameAdrs = (u32)fb; + +	printf("%s\n", panel.modeIdent); + +	/* Start framebuffer */ +	mxs_lcd_init(&panel, &mode, bpp); + +	return (void *)&panel; +} diff --git a/drivers/video/pxa_lcd.c b/drivers/video/pxa_lcd.c index b40ec3689..5e4c6853c 100644 --- a/drivers/video/pxa_lcd.c +++ b/drivers/video/pxa_lcd.c @@ -248,6 +248,38 @@ vidinfo_t panel_info = {  };  #endif /* CONFIG_ACX517AKN */ +#ifdef CONFIG_ACX544AKN + +# define LCD_BPP	LCD_COLOR16 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0	0x003008f9 +# define REG_LCCR3	0x04700007 /* 16bpp */ + +vidinfo_t panel_info = { +	.vl_col		= 320, +	.vl_row		= 320, +	.vl_width	= 320, +	.vl_height	= 320, +	.vl_clkp	= CONFIG_SYS_LOW, +	.vl_oep		= CONFIG_SYS_LOW, +	.vl_hsp		= CONFIG_SYS_LOW, +	.vl_vsp		= CONFIG_SYS_LOW, +	.vl_dp		= CONFIG_SYS_LOW, +	.vl_bpix	= LCD_BPP, +	.vl_lbw		= 0, +	.vl_splt	= 0, +	.vl_clor	= 1, +	.vl_tft		= 1, +	.vl_hpw		= 0x05, +	.vl_blw		= 0x13, +	.vl_elw		= 0x08, +	.vl_vpw		= 0x02, +	.vl_bfw		= 0x07, +	.vl_efw		= 0x05, +}; +#endif /* CONFIG_ACX544AKN */ +  /*----------------------------------------------------------------------*/  #ifdef CONFIG_LQ038J7DH53 @@ -378,7 +410,7 @@ void lcd_initcolregs (void)  #endif /* LCD_MONOCHROME */  /*----------------------------------------------------------------------*/ -void lcd_enable (void) +__weak void lcd_enable(void)  {  } |