diff options
4 files changed, 1361 insertions, 481 deletions
| diff --git a/target/linux/brcm-2.4/files/arch/mips/bcm947xx/include/bcmutils.h b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/include/bcmutils.h new file mode 100644 index 000000000..4c4986ce6 --- /dev/null +++ b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/include/bcmutils.h @@ -0,0 +1,589 @@ +/* + * Misc useful os-independent macros and functions. + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + *  + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * $Id$ + */ + +#ifndef	_bcmutils_h_ +#define	_bcmutils_h_ + +/* ctype replacement */ +#define _BCM_U	0x01	/* upper */ +#define _BCM_L	0x02	/* lower */ +#define _BCM_D	0x04	/* digit */ +#define _BCM_C	0x08	/* cntrl */ +#define _BCM_P	0x10	/* punct */ +#define _BCM_S	0x20	/* white space (space/lf/tab) */ +#define _BCM_X	0x40	/* hex digit */ +#define _BCM_SP	0x80	/* hard space (0x20) */ + +extern const unsigned char bcm_ctype[]; +#define bcm_ismask(x)	(bcm_ctype[(int)(unsigned char)(x)]) + +#define bcm_isalnum(c)	((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_isalpha(c)	((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0) +#define bcm_iscntrl(c)	((bcm_ismask(c)&(_BCM_C)) != 0) +#define bcm_isdigit(c)	((bcm_ismask(c)&(_BCM_D)) != 0) +#define bcm_isgraph(c)	((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_islower(c)	((bcm_ismask(c)&(_BCM_L)) != 0) +#define bcm_isprint(c)	((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0) +#define bcm_ispunct(c)	((bcm_ismask(c)&(_BCM_P)) != 0) +#define bcm_isspace(c)	((bcm_ismask(c)&(_BCM_S)) != 0) +#define bcm_isupper(c)	((bcm_ismask(c)&(_BCM_U)) != 0) +#define bcm_isxdigit(c)	((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0) +#define bcm_tolower(c)	(bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) +#define bcm_toupper(c)	(bcm_islower((c)) ? ((c) + 'A' - 'a') : (c)) + +/* Buffer structure for collecting string-formatted data  +* using bcm_bprintf() API. +* Use bcm_binit() to initialize before use +*/ + +struct bcmstrbuf { +	char *buf;	/* pointer to current position in origbuf */ +	unsigned int size;	/* current (residual) size in bytes */ +	char *origbuf;	/* unmodified pointer to orignal buffer */ +	unsigned int origsize;	/* unmodified orignal buffer size in bytes */ +}; + +/* ** driver-only section ** */ +#ifdef BCMDRIVER +#include <osl.h> + +#define GPIO_PIN_NOTDEFINED 	0x20	/* Pin not defined */ + +/* + * Spin at most 'us' microseconds while 'exp' is true. + * Caller should explicitly test 'exp' when this completes + * and take appropriate error action if 'exp' is still true. + */ +#define SPINWAIT(exp, us) { \ +	uint countdown = (us) + 9; \ +	while ((exp) && (countdown >= 10)) {\ +		OSL_DELAY(10); \ +		countdown -= 10; \ +	} \ +} + + +/* osl multi-precedence packet queue */ +#ifndef PKTQ_LEN_DEFAULT +#define PKTQ_LEN_DEFAULT        128	/* Max 128 packets */ +#endif +#ifndef PKTQ_MAX_PREC +#define PKTQ_MAX_PREC           16	/* Maximum precedence levels */ +#endif + +typedef struct pktq_prec { +	void *head;     /* first packet to dequeue */ +	void *tail;     /* last packet to dequeue */ +	uint16 len;     /* number of queued packets */ +	uint16 max;     /* maximum number of queued packets */ +} pktq_prec_t; + + +/* multi-priority pkt queue */ +struct pktq { +	uint16 num_prec;        /* number of precedences in use */ +	uint16 hi_prec;         /* rapid dequeue hint (>= highest non-empty prec) */ +	uint16 max;             /* total max packets */ +	uint16 len;             /* total number of packets */ +	/* q array must be last since # of elements can be either PKTQ_MAX_PREC or 1 */ +	struct pktq_prec q[PKTQ_MAX_PREC]; +}; + +/* simple, non-priority pkt queue */ +struct spktq { +	uint16 num_prec;        /* number of precedences in use (always 1) */ +	uint16 hi_prec;         /* rapid dequeue hint (>= highest non-empty prec) */ +	uint16 max;             /* total max packets */ +	uint16 len;             /* total number of packets */ +	/* q array must be last since # of elements can be either PKTQ_MAX_PREC or 1 */ +	struct pktq_prec q[1]; +}; + +#define PKTQ_PREC_ITER(pq, prec)        for (prec = (pq)->num_prec - 1; prec >= 0; prec--) + +/* forward definition of ether_addr structure used by some function prototypes */ + +struct ether_addr; + +/* operations on a specific precedence in packet queue */ + +#define pktq_psetmax(pq, prec, _max)    ((pq)->q[prec].max = (_max)) +#define pktq_plen(pq, prec)             ((pq)->q[prec].len) +#define pktq_pavail(pq, prec)           ((pq)->q[prec].max - (pq)->q[prec].len) +#define pktq_pfull(pq, prec)            ((pq)->q[prec].len >= (pq)->q[prec].max) +#define pktq_pempty(pq, prec)           ((pq)->q[prec].len == 0) + +#define pktq_ppeek(pq, prec)            ((pq)->q[prec].head) +#define pktq_ppeek_tail(pq, prec)       ((pq)->q[prec].tail) + +extern void *pktq_penq(struct pktq *pq, int prec, void *p); +extern void *pktq_penq_head(struct pktq *pq, int prec, void *p); +extern void *pktq_pdeq(struct pktq *pq, int prec); +extern void *pktq_pdeq_tail(struct pktq *pq, int prec); +/* Empty the queue at particular precedence level */ +extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir); +/* Remove a specified packet from its queue */ +extern bool pktq_pdel(struct pktq *pq, void *p, int prec); + +/* operations on a set of precedences in packet queue */ + +extern int pktq_mlen(struct pktq *pq, uint prec_bmp); +extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); + +/* operations on packet queue as a whole */ + +#define pktq_len(pq)                    ((int)(pq)->len) +#define pktq_max(pq)                    ((int)(pq)->max) +#define pktq_avail(pq)                  ((int)((pq)->max - (pq)->len)) +#define pktq_full(pq)                   ((pq)->len >= (pq)->max) +#define pktq_empty(pq)                  ((pq)->len == 0) + +/* operations for single precedence queues */ +#define pktenq(pq, p)		pktq_penq(((struct pktq *)pq), 0, (p)) +#define pktenq_head(pq, p)	pktq_penq_head(((struct pktq *)pq), 0, (p)) +#define pktdeq(pq)		pktq_pdeq(((struct pktq *)pq), 0) +#define pktdeq_tail(pq)		pktq_pdeq_tail(((struct pktq *)pq), 0) +#define pktqinit(pq, len) pktq_init(((struct pktq *)pq), 1, len) + +extern void pktq_init(struct pktq *pq, int num_prec, int max_len); +/* prec_out may be NULL if caller is not interested in return value */ +extern void *pktq_deq(struct pktq *pq, int *prec_out); +extern void *pktq_deq_tail(struct pktq *pq, int *prec_out); +extern void *pktq_peek(struct pktq *pq, int *prec_out); +extern void *pktq_peek_tail(struct pktq *pq, int *prec_out); +extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir); /* Empty the entire queue */ +extern int pktq_setmax(struct pktq *pq, int max_len); + +/* externs */ +/* packet */ +extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf); +extern uint pkttotlen(osl_t *osh, void *p); +extern void *pktlast(osl_t *osh, void *p); + +/* Get priority from a packet and pass it back in scb (or equiv) */ +extern uint pktsetprio(void *pkt, bool update_vtag); +#define	PKTPRIO_VDSCP	0x100		/* DSCP prio found after VLAN tag */ +#define	PKTPRIO_VLAN	0x200		/* VLAN prio found */ +#define	PKTPRIO_UPD	0x400		/* DSCP used to update VLAN prio */ +#define	PKTPRIO_DSCP	0x800		/* DSCP prio found */ + +/* string */ +extern int BCMROMFN(bcm_atoi)(char *s); +extern ulong BCMROMFN(bcm_strtoul)(char *cp, char **endp, uint base); +extern char *BCMROMFN(bcmstrstr)(char *haystack, char *needle); +extern char *BCMROMFN(bcmstrcat)(char *dest, const char *src); +extern char *BCMROMFN(bcmstrncat)(char *dest, const char *src, uint size); +extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen); +/* ethernet address */ +extern char *bcm_ether_ntoa(struct ether_addr *ea, char *buf); +extern int BCMROMFN(bcm_ether_atoe)(char *p, struct ether_addr *ea); + +/* ip address */ +struct ipv4_addr; +extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); + +/* delay */ +extern void bcm_mdelay(uint ms); +/* variable access */ +extern char *getvar(char *vars, const char *name); +extern int getintvar(char *vars, const char *name); +extern uint getgpiopin(char *vars, char *pin_name, uint def_pin); +#ifdef BCMPERFSTATS +extern void bcm_perf_enable(void); +extern void bcmstats(char *fmt); +extern void bcmlog(char *fmt, uint a1, uint a2); +extern void bcmdumplog(char *buf, int size); +extern int bcmdumplogent(char *buf, uint idx); +#else +#define bcm_perf_enable() +#define bcmstats(fmt) +#define	bcmlog(fmt, a1, a2) +#define	bcmdumplog(buf, size)	*buf = '\0' +#define	bcmdumplogent(buf, idx)	-1 +#endif /* BCMPERFSTATS */ +extern char *bcm_nvram_vars(uint *length); +extern int bcm_nvram_cache(void *sbh); + +/* Support for sharing code across in-driver iovar implementations. + * The intent is that a driver use this structure to map iovar names + * to its (private) iovar identifiers, and the lookup function to + * find the entry.  Macros are provided to map ids and get/set actions + * into a single number space for a switch statement. + */ + +/* iovar structure */ +typedef struct bcm_iovar { +	const char *name;	/* name for lookup and display */ +	uint16 varid;		/* id for switch */ +	uint16 flags;		/* driver-specific flag bits */ +	uint16 type;		/* base type of argument */ +	uint16 minlen;		/* min length for buffer vars */ +} bcm_iovar_t; + +/* varid definitions are per-driver, may use these get/set bits */ + +/* IOVar action bits for id mapping */ +#define IOV_GET 0 /* Get an iovar */ +#define IOV_SET 1 /* Set an iovar */ + +/* Varid to actionid mapping */ +#define IOV_GVAL(id)		((id)*2) +#define IOV_SVAL(id)		(((id)*2)+IOV_SET) +#define IOV_ISSET(actionid)	((actionid & IOV_SET) == IOV_SET) + +/* flags are per-driver based on driver attributes */ + +extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name); +extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set); + +#endif	/* BCMDRIVER */ + +/* Base type definitions */ +#define IOVT_VOID	0	/* no value (implictly set only) */ +#define IOVT_BOOL	1	/* any value ok (zero/nonzero) */ +#define IOVT_INT8	2	/* integer values are range-checked */ +#define IOVT_UINT8	3	/* unsigned int 8 bits */ +#define IOVT_INT16	4	/* int 16 bits */ +#define IOVT_UINT16	5	/* unsigned int 16 bits */ +#define IOVT_INT32	6	/* int 32 bits */ +#define IOVT_UINT32	7	/* unsigned int 32 bits */ +#define IOVT_BUFFER	8	/* buffer is size-checked as per minlen */ +#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER) + +/* Initializer for IOV type strings */ +#define BCM_IOV_TYPE_INIT { \ +	"void", \ +	"bool", \ +	"int8", \ +	"uint8", \ +	"int16", \ +	"uint16", \ +	"int32", \ +	"uint32", \ +	"buffer", \ +	"" } + +#define BCM_IOVT_IS_INT(type) (\ +	(type == IOVT_BOOL) || \ +	(type == IOVT_INT8) || \ +	(type == IOVT_UINT8) || \ +	(type == IOVT_INT16) || \ +	(type == IOVT_UINT16) || \ +	(type == IOVT_INT32) || \ +	(type == IOVT_UINT32)) + +/* ** driver/apps-shared section ** */ + +#define BCME_STRLEN 		64	/* Max string length for BCM errors */ +#define VALID_BCMERROR(e)  ((e <= 0) && (e >= BCME_LAST)) + + +/* + * error codes could be added but the defined ones shouldn't be changed/deleted + * these error codes are exposed to the user code + * when ever a new error code is added to this list + * please update errorstring table with the related error string and + * update osl files with os specific errorcode map +*/ + +#define BCME_OK				0	/* Success */ +#define BCME_ERROR			-1	/* Error generic */ +#define BCME_BADARG			-2	/* Bad Argument */ +#define BCME_BADOPTION			-3	/* Bad option */ +#define BCME_NOTUP			-4	/* Not up */ +#define BCME_NOTDOWN			-5	/* Not down */ +#define BCME_NOTAP			-6	/* Not AP */ +#define BCME_NOTSTA			-7	/* Not STA  */ +#define BCME_BADKEYIDX			-8	/* BAD Key Index */ +#define BCME_RADIOOFF 			-9	/* Radio Off */ +#define BCME_NOTBANDLOCKED		-10	/* Not  band locked */ +#define BCME_NOCLK			-11	/* No Clock */ +#define BCME_BADRATESET			-12	/* BAD Rate valueset */ +#define BCME_BADBAND			-13	/* BAD Band */ +#define BCME_BUFTOOSHORT		-14	/* Buffer too short */ +#define BCME_BUFTOOLONG			-15	/* Buffer too long */ +#define BCME_BUSY			-16	/* Busy */ +#define BCME_NOTASSOCIATED		-17	/* Not Associated */ +#define BCME_BADSSIDLEN			-18	/* Bad SSID len */ +#define BCME_OUTOFRANGECHAN		-19	/* Out of Range Channel */ +#define BCME_BADCHAN			-20	/* Bad Channel */ +#define BCME_BADADDR			-21	/* Bad Address */ +#define BCME_NORESOURCE			-22	/* Not Enough Resources */ +#define BCME_UNSUPPORTED		-23	/* Unsupported */ +#define BCME_BADLEN			-24	/* Bad length */ +#define BCME_NOTREADY			-25	/* Not Ready */ +#define BCME_EPERM			-26	/* Not Permitted */ +#define BCME_NOMEM			-27	/* No Memory */ +#define BCME_ASSOCIATED			-28	/* Associated */ +#define BCME_RANGE			-29	/* Not In Range */ +#define BCME_NOTFOUND			-30	/* Not Found */ +#define BCME_WME_NOT_ENABLED		-31	/* WME Not Enabled */ +#define BCME_TSPEC_NOTFOUND		-32	/* TSPEC Not Found */ +#define BCME_ACM_NOTSUPPORTED		-33	/* ACM Not Supported */ +#define BCME_NOT_WME_ASSOCIATION	-34	/* Not WME Association */ +#define BCME_SDIO_ERROR			-35	/* SDIO Bus Error */ +#define BCME_DONGLE_DOWN		-36	/* Dongle Not Accessible */ +#define BCME_VERSION			-37 /* Incorrect version */ +#define BCME_LAST			BCME_VERSION + +/* These are collection of BCME Error strings */ +#define BCMERRSTRINGTABLE {		\ +	"OK",				\ +	"Undefined error",		\ +	"Bad Argument",			\ +	"Bad Option",			\ +	"Not up",			\ +	"Not down",			\ +	"Not AP",			\ +	"Not STA",			\ +	"Bad Key Index",		\ +	"Radio Off",			\ +	"Not band locked",		\ +	"No clock",			\ +	"Bad Rate valueset",		\ +	"Bad Band",			\ +	"Buffer too short",		\ +	"Buffer too long",		\ +	"Busy",				\ +	"Not Associated",		\ +	"Bad SSID len",			\ +	"Out of Range Channel",		\ +	"Bad Channel",			\ +	"Bad Address",			\ +	"Not Enough Resources",		\ +	"Unsupported",			\ +	"Bad length",			\ +	"Not Ready",			\ +	"Not Permitted",		\ +	"No Memory",			\ +	"Associated",			\ +	"Not In Range",			\ +	"Not Found",			\ +	"WME Not Enabled",		\ +	"TSPEC Not Found",		\ +	"ACM Not Supported",		\ +	"Not WME Association",		\ +	"SDIO Bus Error",		\ +	"Dongle Not Accessible",	\ +	"Incorrect version"	\ +} + +#ifndef ABS +#define	ABS(a)			(((a) < 0)?-(a):(a)) +#endif /* ABS */ + +#ifndef MIN +#define	MIN(a, b)		(((a) < (b))?(a):(b)) +#endif /* MIN */ + +#ifndef MAX +#define	MAX(a, b)		(((a) > (b))?(a):(b)) +#endif /* MAX */ + +#define CEIL(x, y)		(((x) + ((y)-1)) / (y)) +#define	ROUNDUP(x, y)		((((x)+((y)-1))/(y))*(y)) +#define	ISALIGNED(a, x)		(((a) & ((x)-1)) == 0) +#define	ISPOWEROF2(x)		((((x)-1)&(x)) == 0) +#define VALID_MASK(mask)	!((mask) & ((mask) + 1)) +#ifndef OFFSETOF +#define	OFFSETOF(type, member)	((uint)(uintptr)&((type *)0)->member) +#endif /* OFFSETOF */ +#ifndef ARRAYSIZE +#define ARRAYSIZE(a)		(sizeof(a)/sizeof(a[0])) +#endif + +/* bit map related macros */ +#ifndef setbit +#ifndef NBBY		    /* the BSD family defines NBBY */ +#define	NBBY	8	/* 8 bits per byte */ +#endif /* #ifndef NBBY */ +#define	setbit(a, i)	(((uint8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define	clrbit(a, i)	(((uint8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define	isset(a, i)	(((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define	isclr(a, i)	((((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif /* setbit */ + +#define	NBITS(type)	(sizeof(type) * 8) +#define NBITVAL(nbits)	(1 << (nbits)) +#define MAXBITVAL(nbits)	((1 << (nbits)) - 1) +#define	NBITMASK(nbits)	MAXBITVAL(nbits) +#define MAXNBVAL(nbyte)	MAXBITVAL((nbyte) * 8) + +/* basic mux operation - can be optimized on several architectures */ +#define MUX(pred, true, false) ((pred) ? (true) : (false)) + +/* modulo inc/dec - assumes x E [0, bound - 1] */ +#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1) +#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) + +/* modulo inc/dec, bound = 2^k */ +#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) +#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) + +/* modulo add/sub - assumes x, y E [0, bound - 1] */ +#define MODADD(x, y, bound) \ +    MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y)) +#define MODSUB(x, y, bound) \ +    MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y)) + +/* module add/sub, bound = 2^k */ +#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) +#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) + +/* crc defines */ +#define CRC8_INIT_VALUE  0xff		/* Initial CRC8 checksum value */ +#define CRC8_GOOD_VALUE  0x9f		/* Good final CRC8 checksum value */ +#define CRC16_INIT_VALUE 0xffff		/* Initial CRC16 checksum value */ +#define CRC16_GOOD_VALUE 0xf0b8		/* Good final CRC16 checksum value */ +#define CRC32_INIT_VALUE 0xffffffff	/* Initial CRC32 checksum value */ +#define CRC32_GOOD_VALUE 0xdebb20e3	/* Good final CRC32 checksum value */ + +/* bcm_format_flags() bit description structure */ +typedef struct bcm_bit_desc { +	uint32	bit; +	const char* name; +} bcm_bit_desc_t; + +/* tag_ID/length/value_buffer tuple */ +typedef struct bcm_tlv { +	uint8	id; +	uint8	len; +	uint8	data[1]; +} bcm_tlv_t; + +/* Check that bcm_tlv_t fits into the given buflen */ +#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len)) + +/* buffer length for ethernet address from bcm_ether_ntoa() */ +#define ETHER_ADDR_STR_LEN	18	/* 18-bytes of Ethernet address buffer length */ + +/* unaligned load and store macros */ +#ifdef IL_BIGENDIAN +static INLINE uint32 +load32_ua(uint8 *a) +{ +	return ((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]); +} + +static INLINE void +store32_ua(uint8 *a, uint32 v) +{ +	a[0] = (v >> 24) & 0xff; +	a[1] = (v >> 16) & 0xff; +	a[2] = (v >> 8) & 0xff; +	a[3] = v & 0xff; +} + +static INLINE uint16 +load16_ua(uint8 *a) +{ +	return ((a[0] << 8) | a[1]); +} + +static INLINE void +store16_ua(uint8 *a, uint16 v) +{ +	a[0] = (v >> 8) & 0xff; +	a[1] = v & 0xff; +} + +#else /* IL_BIGENDIAN */ + +static INLINE uint32 +load32_ua(uint8 *a) +{ +	return ((a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]); +} + +static INLINE void +store32_ua(uint8 *a, uint32 v) +{ +	a[3] = (v >> 24) & 0xff; +	a[2] = (v >> 16) & 0xff; +	a[1] = (v >> 8) & 0xff; +	a[0] = v & 0xff; +} + +static INLINE uint16 +load16_ua(uint8 *a) +{ +	return ((a[1] << 8) | a[0]); +} + +static INLINE void +store16_ua(uint8 *a, uint16 v) +{ +	a[1] = (v >> 8) & 0xff; +	a[0] = v & 0xff; +} + +#endif /* IL_BIGENDIAN */ + +/* externs */ +/* crc */ +extern uint8 BCMROMFN(hndcrc8)(uint8 *p, uint nbytes, uint8 crc); +extern uint16 BCMROMFN(hndcrc16)(uint8 *p, uint nbytes, uint16 crc); +extern uint32 BCMROMFN(hndcrc32)(uint8 *p, uint nbytes, uint32 crc); +/* format/print */ +extern char *bcm_brev_str(uint16 brev, char *buf); +extern void printfbig(char *buf); + +/* IE parsing */ +extern bcm_tlv_t *BCMROMFN(bcm_next_tlv)(bcm_tlv_t *elt, int *buflen); +extern bcm_tlv_t *BCMROMFN(bcm_parse_tlvs)(void *buf, int buflen, uint key); +extern bcm_tlv_t *BCMROMFN(bcm_parse_ordered_tlvs)(void *buf, int buflen, uint key); + +/* bcmerror */ +extern const char *bcmerrorstr(int bcmerror); + +/* multi-bool data type: set of bools, mbool is true if any is set */ +typedef uint32 mbool; +#define mboolset(mb, bit)		((mb) |= (bit))		/* set one bool */ +#define mboolclr(mb, bit)		((mb) &= ~(bit))	/* clear one bool */ +#define mboolisset(mb, bit)		(((mb) & (bit)) != 0)	/* TRUE if one bool is set */ +#define	mboolmaskset(mb, mask, val)	((mb) = (((mb) & ~(mask)) | (val))) + +/* power conversion */ +extern uint16 BCMROMFN(bcm_qdbm_to_mw)(uint8 qdbm); +extern uint8 BCMROMFN(bcm_mw_to_qdbm)(uint16 mw); + +/* generic datastruct to help dump routines */ +struct fielddesc { +	const char *nameandfmt; +	uint32 	offset; +	uint32 	len; +}; + +extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size); +extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...); + +typedef  uint32 (*readreg_rtn)(void *arg0, void *arg1, uint32 offset); +extern uint bcmdumpfields(readreg_rtn func_ptr, void *arg0, void *arg1, struct fielddesc *str, +                          char *buf, uint32 bufsize); + +extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len); +extern uint BCMROMFN(bcm_bitcount)(uint8 *bitmap, uint bytelength); + +#ifdef BCMDBG_PKT      /* pkt logging for debugging */ +#define PKTLIST_SIZE 1000 +typedef struct { +	void *list[PKTLIST_SIZE]; /* List of pointers to packets */ +	uint count; /* Total count of the packets */ +} pktlist_info_t; + +extern void pktlist_add(pktlist_info_t *pktlist, void *p); +extern void pktlist_remove(pktlist_info_t *pktlist, void *p); +extern char* pktlist_dump(pktlist_info_t *pktlist, char *buf); +#endif  /* BCMDBG_PKT */ + +#endif	/* _bcmutils_h_ */ diff --git a/target/linux/brcm-2.4/files/arch/mips/bcm947xx/sflash.c b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/sflash.c new file mode 100644 index 000000000..9bfb8b42a --- /dev/null +++ b/target/linux/brcm-2.4/files/arch/mips/bcm947xx/sflash.c @@ -0,0 +1,508 @@ +/* + * Broadcom SiliconBackplane chipcommon serial flash interface + * + * Copyright 2007, Broadcom Corporation + * All Rights Reserved. + *  + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <typedefs.h> +#include <osl.h> +#include "include/bcmutils.h" +#include <sbutils.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <bcmdevs.h> +#include <sflash.h> + +/* Private global state */ +static struct sflash sflash; + +/* Issue a serial flash command */ +static INLINE void +sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode) +{ +	W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode); +	while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY); +} + +/* Initialize serial flash access */ +struct sflash * +sflash_init(sb_t *sbh, chipcregs_t *cc) +{ +	uint32 id, id2; +	osl_t *osh; + +	ASSERT(sbh); + +	osh = sb_osh(sbh); + +	bzero(&sflash, sizeof(sflash)); + +	sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK; + +	switch (sflash.type) { +	case SFLASH_ST: +		/* Probe for ST chips */ +		sflash_cmd(osh, cc, SFLASH_ST_DP); +		sflash_cmd(osh, cc, SFLASH_ST_RES); +		id = R_REG(osh, &cc->flashdata); +		switch (id) { +		case 0x11: +			/* ST M25P20 2 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 4; +			break; +		case 0x12: +			/* ST M25P40 4 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 8; +			break; +		case 0x13: +			/* ST M25P80 8 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 16; +			break; +		case 0x14: +			/* ST M25P16 16 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 32; +			break; +		case 0x15: +			/* ST M25P32 32 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 64; +			break; +		case 0x16: +			/* ST M25P64 64 Mbit Serial Flash */ +			sflash.blocksize = 64 * 1024; +			sflash.numblocks = 128; +			break; +		case 0xbf: +			W_REG(osh, &cc->flashaddress, 1); +			sflash_cmd(osh, cc, SFLASH_ST_RES); +			id2 = R_REG(osh, &cc->flashdata); +			if (id2 == 0x44) { +				/* SST M25VF80 4 Mbit Serial Flash */ +				sflash.blocksize = 64 * 1024; +				sflash.numblocks = 8; +			} +			break; +		} +		break; + +	case SFLASH_AT: +		/* Probe for Atmel chips */ +		sflash_cmd(osh, cc, SFLASH_AT_STATUS); +		id = R_REG(osh, &cc->flashdata) & 0x3c; +		switch (id) { +		case 0xc: +			/* Atmel AT45DB011 1Mbit Serial Flash */ +			sflash.blocksize = 256; +			sflash.numblocks = 512; +			break; +		case 0x14: +			/* Atmel AT45DB021 2Mbit Serial Flash */ +			sflash.blocksize = 256; +			sflash.numblocks = 1024; +			break; +		case 0x1c: +			/* Atmel AT45DB041 4Mbit Serial Flash */ +			sflash.blocksize = 256; +			sflash.numblocks = 2048; +			break; +		case 0x24: +			/* Atmel AT45DB081 8Mbit Serial Flash */ +			sflash.blocksize = 256; +			sflash.numblocks = 4096; +			break; +		case 0x2c: +			/* Atmel AT45DB161 16Mbit Serial Flash */ +			sflash.blocksize = 512; +			sflash.numblocks = 4096; +			break; +		case 0x34: +			/* Atmel AT45DB321 32Mbit Serial Flash */ +			sflash.blocksize = 512; +			sflash.numblocks = 8192; +			break; +		case 0x3c: +			/* Atmel AT45DB642 64Mbit Serial Flash */ +			sflash.blocksize = 1024; +			sflash.numblocks = 8192; +			break; +		} +		break; +	} + +	sflash.size = sflash.blocksize * sflash.numblocks; +	return sflash.size ? &sflash : NULL; +} + +/* Read len bytes starting at offset into buf. Returns number of bytes read. */ +int +sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf) +{ +	uint8 *from, *to; +	int cnt, i; +	osl_t *osh; + +	ASSERT(sbh); + +	if (!len) +		return 0; + +	if ((offset + len) > sflash.size) +		return -22; + +	if ((len >= 4) && (offset & 3)) +		cnt = 4 - (offset & 3); +	else if ((len >= 4) && ((uintptr)buf & 3)) +		cnt = 4 - ((uintptr)buf & 3); +	else +		cnt = len; + +	osh = sb_osh(sbh); + +	from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset); +	to = (uint8 *)buf; + +	if (cnt < 4) { +		for (i = 0; i < cnt; i ++) { +			*to = R_REG(osh, from); +			from ++; +			to ++; +		} +		return cnt; +	} + +	while (cnt >= 4) { +		*(uint32 *)to = R_REG(osh, (uint32 *)from); +		from += 4; +		to += 4; +		cnt -= 4; +	} + +	return (len - cnt); +} + +/* Poll for command completion. Returns zero when complete. */ +int +sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset) +{ +	osl_t *osh; + +	ASSERT(sbh); + +	osh = sb_osh(sbh); + +	if (offset >= sflash.size) +		return -22; + +	switch (sflash.type) { +	case SFLASH_ST: +		/* Check for ST Write In Progress bit */ +		sflash_cmd(osh, cc, SFLASH_ST_RDSR); +		return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP; +	case SFLASH_AT: +		/* Check for Atmel Ready bit */ +		sflash_cmd(osh, cc, SFLASH_AT_STATUS); +		return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY); +	} + +	return 0; +} + +/* Write len bytes starting at offset into buf. Returns number of bytes + * written. Caller should poll for completion. + */ +int +sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf) +{ +	struct sflash *sfl; +	int ret = 0; +	bool is4712b0; +	uint32 page, byte, mask; +	osl_t *osh; + +	ASSERT(sbh); + +	osh = sb_osh(sbh); + +	if (!len) +		return 0; + +	if ((offset + len) > sflash.size) +		return -22; + +	sfl = &sflash; +	switch (sfl->type) { +	case SFLASH_ST: +		is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3); +		/* Enable writes */ +		sflash_cmd(osh, cc, SFLASH_ST_WREN); +		if (is4712b0) { +			mask = 1 << 14; +			W_REG(osh, &cc->flashaddress, offset); +			W_REG(osh, &cc->flashdata, *buf++); +			/* Set chip select */ +			OR_REG(osh, &cc->gpioout, mask); +			/* Issue a page program with the first byte */ +			sflash_cmd(osh, cc, SFLASH_ST_PP); +			ret = 1; +			offset++; +			len--; +			while (len > 0) { +				if ((offset & 255) == 0) { +					/* Page boundary, drop cs and return */ +					AND_REG(osh, &cc->gpioout, ~mask); +					if (!sflash_poll(sbh, cc, offset)) { +						/* Flash rejected command */ +						return -11; +					} +					return ret; +				} else { +					/* Write single byte */ +					sflash_cmd(osh, cc, *buf++); +				} +				ret++; +				offset++; +				len--; +			} +			/* All done, drop cs if needed */ +			if ((offset & 255) != 1) { +				/* Drop cs */ +				AND_REG(osh, &cc->gpioout, ~mask); +				if (!sflash_poll(sbh, cc, offset)) { +					/* Flash rejected command */ +					return -12; +				} +			} +		} else if ( (sbh->ccrev >= 20) && (len != 1) ) { +		//} else if ( sbh->ccrev >= 20 ) {              /* foxconn modified by EricHuang, 05/24/2007 */ +			W_REG(NULL, &cc->flashaddress, offset); +			W_REG(NULL, &cc->flashdata, *buf++); +			/* Issue a page program with CSA bit set */ +			sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); +			ret = 1; +			offset++; +			len--; +			while (len > 0) { +				if ((offset & 255) == 0) { +					/* Page boundary, poll droping cs and return */ +					W_REG(NULL, &cc->flashcontrol, 0); +					/* wklin added start, 06/08/2007 */ +					W_REG(NULL, &cc->flashcontrol, 0); +					OSL_DELAY(1); +					/* wklin added end, 06/08/2007 */ +					/* wklin rmeoved start, 06/08/2007 */ +#if 0 +					if (!sflash_poll(sbh, cc, offset)) { +						/* Flash rejected command */ +						return -11; +					} +#endif					 +                                        /* wklin removed end, 06/08/2007 */ +					return ret; +				} else { +					/* Write single byte */ +					sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++); +				} +				ret++; +				offset++; +				len--; +			} +			/* All done, drop cs if needed */ +			if ((offset & 255) != 1) { +				/* Drop cs, poll */ +				W_REG(NULL, &cc->flashcontrol, 0); +				/* wklin added start, 06/08/2007 */ +				W_REG(NULL, &cc->flashcontrol, 0); +				OSL_DELAY(1); +				/* wklin added end, 06/08/2007 */ +				/* wklin removed start, 06/08/2007 */ +#if 0				 +				if (!sflash_poll(sbh, cc, offset)) { +					/* Flash rejected command */ +					return -12; +				} +#endif +                                /* wklin removed end, 06/08/2007 */ +			} +		} else { +			ret = 1; +			W_REG(osh, &cc->flashaddress, offset); +			W_REG(osh, &cc->flashdata, *buf); +			/* Page program */ +			sflash_cmd(osh, cc, SFLASH_ST_PP); +		} +		break; +	case SFLASH_AT: +		mask = sfl->blocksize - 1; +		page = (offset & ~mask) << 1; +		byte = offset & mask; +		/* Read main memory page into buffer 1 */ +		if (byte || (len < sfl->blocksize)) { +			W_REG(osh, &cc->flashaddress, page); +			sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); +			/* 250 us for AT45DB321B */ +			SPINWAIT(sflash_poll(sbh, cc, offset), 1000); +			ASSERT(!sflash_poll(sbh, cc, offset)); +		} +		/* Write into buffer 1 */ +		for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { +			W_REG(osh, &cc->flashaddress, byte++); +			W_REG(osh, &cc->flashdata, *buf++); +			sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); +		} +		/* Write buffer 1 into main memory page */ +		W_REG(osh, &cc->flashaddress, page); +		sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); +		break; +	} + +	return ret; +} + +/* Erase a region. Returns number of bytes scheduled for erasure. + * Caller should poll for completion. + */ +int +sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset) +{ +	struct sflash *sfl; +	osl_t *osh; + +	ASSERT(sbh); + +	osh = sb_osh(sbh); + +	if (offset >= sflash.size) +		return -22; + +	sfl = &sflash; +	switch (sfl->type) { +	case SFLASH_ST: +		sflash_cmd(osh, cc, SFLASH_ST_WREN); +		W_REG(osh, &cc->flashaddress, offset); +		sflash_cmd(osh, cc, SFLASH_ST_SE); +		return sfl->blocksize; +	case SFLASH_AT: +		W_REG(osh, &cc->flashaddress, offset << 1); +		sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE); +		return sfl->blocksize; +	} + +	return 0; +} + +/* + * writes the appropriate range of flash, a NULL buf simply erases + * the region of flash + */ +int +sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf) +{ +	struct sflash *sfl; +	uchar *block = NULL, *cur_ptr, *blk_ptr; +	uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; +	uint blk_offset, blk_len, copied; +	int bytes, ret = 0; +	osl_t *osh; + +	ASSERT(sbh); + +	osh = sb_osh(sbh); + +	/* Check address range */ +	if (len <= 0) +		return 0; + +	sfl = &sflash; +	if ((offset + len) > sfl->size) +		return -1; + +	blocksize = sfl->blocksize; +	mask = blocksize - 1; + +	/* Allocate a block of mem */ +	if (!(block = MALLOC(osh, blocksize))) +		return -1; + +	while (len) { +		/* Align offset */ +		cur_offset = offset & ~mask; +		cur_length = blocksize; +		cur_ptr = block; + +		remainder = blocksize - (offset & mask); +		if (len < remainder) +			cur_retlen = len; +		else +			cur_retlen = remainder; + +		/* buf == NULL means erase only */ +		if (buf) { +			/* Copy existing data into holding block if necessary */ +			if ((offset & mask) || (len < blocksize)) { +				blk_offset = cur_offset; +				blk_len = cur_length; +				blk_ptr = cur_ptr; + +				/* Copy entire block */ +				while (blk_len) { +					copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr); +					blk_offset += copied; +					blk_len -= copied; +					blk_ptr += copied; +				} +			} + +			/* Copy input data into holding block */ +			memcpy(cur_ptr + (offset & mask), buf, cur_retlen); +		} + +		/* Erase block */ +		if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0) +			goto done; +		while (sflash_poll(sbh, cc, (uint) cur_offset)); + +		/* buf == NULL means erase only */ +		if (!buf) { +			offset += cur_retlen; +			len -= cur_retlen; +			continue; +		} + +		/* Write holding block */ +		while (cur_length > 0) { +			if ((bytes = sflash_write(sbh, cc, +			                          (uint) cur_offset, +			                          (uint) cur_length, +			                          (uchar *) cur_ptr)) < 0) { +				ret = bytes; +				goto done; +			} +			while (sflash_poll(sbh, cc, (uint) cur_offset)); +			cur_offset += bytes; +			cur_length -= bytes; +			cur_ptr += bytes; +		} + +		offset += cur_retlen; +		len -= cur_retlen; +		buf += cur_retlen; +	} + +	ret = len; +done: +	if (block) +		MFREE(osh, block, blocksize); +	return ret; +} diff --git a/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c b/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c index 7ffb00478..a8aab7bd9 100644 --- a/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c +++ b/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c @@ -1,530 +1,303 @@  /*   * Broadcom SiliconBackplane chipcommon serial flash interface   * - * Copyright 2007, Broadcom Corporation - * All Rights Reserved. - *  - * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY - * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM - * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * Copyright 2006, Broadcom Corporation       + * All Rights Reserved.       + *        + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY       + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM       + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS       + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.         * + * $Id$   */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <asm/io.h> +  #include <typedefs.h>  #include <osl.h> +// #include <bcmutils.h> +#include <bcmdevs.h> +#include <bcmnvram.h>  #include <sbutils.h>  #include <sbconfig.h>  #include <sbchipc.h> -#include <bcmdevs.h>  #include <sflash.h> +#ifdef CONFIG_MTD_PARTITIONS +extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); +#endif + +struct sflash_mtd { +	sb_t *sbh; +	chipcregs_t *cc; +	struct semaphore lock; +	struct mtd_info mtd; +	struct mtd_erase_region_info region; +}; +  /* Private global state */ -static struct sflash sflash; +static struct sflash_mtd sflash; -/* Issue a serial flash command */ -static INLINE void -sflash_cmd (osl_t * osh, chipcregs_t * cc, uint opcode) +static int +sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)  { -  W_REG (osh, &cc->flashcontrol, SFLASH_START | opcode); -  while (R_REG (osh, &cc->flashcontrol) & SFLASH_BUSY); -} +	int now = jiffies; +	int ret = 0; -/* Initialize serial flash access */ -struct sflash * -sflash_init (sb_t * sbh, chipcregs_t * cc) -{ -  uint32 id, id2; -  osl_t *osh; - -  ASSERT (sbh); - -  osh = sb_osh (sbh); - -  bzero (&sflash, sizeof (sflash)); - -  sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK; - -  switch (sflash.type) -    { -    case SFLASH_ST: -      /* Probe for ST chips */ -      sflash_cmd (osh, cc, SFLASH_ST_DP); -      sflash_cmd (osh, cc, SFLASH_ST_RES); -      id = R_REG (osh, &cc->flashdata); -      switch (id) -	{ -	case 0x11: -	  /* ST M25P20 2 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 4; -	  break; -	case 0x12: -	  /* ST M25P40 4 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 8; -	  break; -	case 0x13: -	  /* ST M25P80 8 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 16; -	  break; -	case 0x14: -	  /* ST M25P16 16 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 32; -	  break; -	case 0x15: -	  /* ST M25P32 32 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 64; -	  break; -	case 0x16: -	  /* ST M25P64 64 Mbit Serial Flash */ -	  sflash.blocksize = 64 * 1024; -	  sflash.numblocks = 128; -	  break; -	case 0xbf: -	  W_REG (osh, &cc->flashaddress, 1); -	  sflash_cmd (osh, cc, SFLASH_ST_RES); -	  id2 = R_REG (osh, &cc->flashdata); -	  if (id2 == 0x44) -	    { -	      /* SST M25VF80 4 Mbit Serial Flash */ -	      sflash.blocksize = 64 * 1024; -	      sflash.numblocks = 8; -	    } -	  break; -	} -      break; - -    case SFLASH_AT: -      /* Probe for Atmel chips */ -      sflash_cmd (osh, cc, SFLASH_AT_STATUS); -      id = R_REG (osh, &cc->flashdata) & 0x3c; -      switch (id) -	{ -	case 0xc: -	  /* Atmel AT45DB011 1Mbit Serial Flash */ -	  sflash.blocksize = 256; -	  sflash.numblocks = 512; -	  break; -	case 0x14: -	  /* Atmel AT45DB021 2Mbit Serial Flash */ -	  sflash.blocksize = 256; -	  sflash.numblocks = 1024; -	  break; -	case 0x1c: -	  /* Atmel AT45DB041 4Mbit Serial Flash */ -	  sflash.blocksize = 256; -	  sflash.numblocks = 2048; -	  break; -	case 0x24: -	  /* Atmel AT45DB081 8Mbit Serial Flash */ -	  sflash.blocksize = 256; -	  sflash.numblocks = 4096; -	  break; -	case 0x2c: -	  /* Atmel AT45DB161 16Mbit Serial Flash */ -	  sflash.blocksize = 512; -	  sflash.numblocks = 4096; -	  break; -	case 0x34: -	  /* Atmel AT45DB321 32Mbit Serial Flash */ -	  sflash.blocksize = 512; -	  sflash.numblocks = 8192; -	  break; -	case 0x3c: -	  /* Atmel AT45DB642 64Mbit Serial Flash */ -	  sflash.blocksize = 1024; -	  sflash.numblocks = 8192; -	  break; +	for (;;) { +		if (!sflash_poll(sflash->sbh, sflash->cc, offset)) { +			ret = 0; +			break; +		} +		if (time_after(jiffies, now + timeout)) { +			printk(KERN_ERR "sflash: timeout\n"); +			ret = -ETIMEDOUT; +			break; +		} +		if (current->need_resched) { +			set_current_state(TASK_UNINTERRUPTIBLE); +			schedule_timeout(timeout / 10); +		} else +			udelay(1);  	} -      break; -    } -  sflash.size = sflash.blocksize * sflash.numblocks; -  return sflash.size ? &sflash : NULL; +	return ret;  } -/* Read len bytes starting at offset into buf. Returns number of bytes read. */ -int -sflash_read (sb_t * sbh, chipcregs_t * cc, uint offset, uint len, uchar * buf) +static int +sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)  { -  uint8 *from, *to; -  int cnt, i; -  osl_t *osh; - -  ASSERT (sbh); - -  if (!len) -    return 0; +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int bytes, ret = 0; + +	/* Check address range */ +	if (len == 0){ +	 *retlen = 0; +		return 0; + } +	if (!len) +		return 0; +	if ((from + len) > mtd->size) +		return -EINVAL; +	 +	down(&sflash->lock); + +	*retlen = 0; +	while (len) { +		if ((bytes = sflash_read(sflash->sbh, sflash->cc, (uint) from, len, buf)) < 0) { +			ret = bytes; +			break; +		} +		from += (loff_t) bytes; +		len -= bytes; +		buf += bytes; +		*retlen += bytes; +	} -  if ((offset + len) > sflash.size) -    return -22; +	up(&sflash->lock); -  if ((len >= 4) && (offset & 3)) -    cnt = 4 - (offset & 3); -  else if ((len >= 4) && ((uintptr) buf & 3)) -    cnt = 4 - ((uintptr) buf & 3); -  else -    cnt = len; +	return ret; +} -  osh = sb_osh (sbh); +static int +sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int bytes, ret = 0; + +	/* Check address range */ +	if (len == 0){ +	 *retlen = 0; +		return 0; + } +	if (!len) +		return 0; +	if ((to + len) > mtd->size) +		return -EINVAL; + +	down(&sflash->lock); + +	*retlen = 0; +	while (len) { +		if ((bytes = sflash_write(sflash->sbh, sflash->cc, (uint)to, (uint)len, buf)) < 0) { +			ret = bytes; +			break; +		} +		if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10))) +			break; +		to += (loff_t) bytes; +		len -= bytes; +		buf += bytes; +		*retlen += bytes; +	} -  from = (uint8 *) (uintptr) OSL_UNCACHED (SB_FLASH2 + offset); -  to = (uint8 *) buf; +	up(&sflash->lock); -  if (cnt < 4) -    { -      for (i = 0; i < cnt; i++) -	{ -	  *to = R_REG (osh, from); -	  from++; -	  to++; -	} -      return cnt; -    } - -  while (cnt >= 4) -    { -      *(uint32 *) to = R_REG (osh, (uint32 *) from); -      from += 4; -      to += 4; -      cnt -= 4; -    } - -  return (len - cnt); +	return ret;  } -/* Poll for command completion. Returns zero when complete. */ -int -sflash_poll (sb_t * sbh, chipcregs_t * cc, uint offset) +static int +sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)  { -  osl_t *osh; - -  ASSERT (sbh); +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int i, j, ret = 0; +	unsigned int addr, len; + +	/* Check address range */ +	if (!erase->len) +		return 0; +	if ((erase->addr + erase->len) > mtd->size) +		return -EINVAL; + +	addr = erase->addr; +	len = erase->len; + +	down(&sflash->lock); + +	/* Ensure that requested region is aligned */ +	for (i = 0; i < mtd->numeraseregions; i++) { +		for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { +			if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j && +			    len >= mtd->eraseregions[i].erasesize) { +				if ((ret = sflash_erase(sflash->sbh, sflash->cc, addr)) < 0) +					break; +				if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ))) +					break; +				addr += mtd->eraseregions[i].erasesize; +				len -= mtd->eraseregions[i].erasesize; +			} +		} +		if (ret) +			break; +	} -  osh = sb_osh (sbh); +	up(&sflash->lock); -  if (offset >= sflash.size) -    return -22; +	/* Set erase status */ +	if (ret) +		erase->state = MTD_ERASE_FAILED; +	else  +		erase->state = MTD_ERASE_DONE; -  switch (sflash.type) -    { -    case SFLASH_ST: -      /* Check for ST Write In Progress bit */ -      sflash_cmd (osh, cc, SFLASH_ST_RDSR); -      return R_REG (osh, &cc->flashdata) & SFLASH_ST_WIP; -    case SFLASH_AT: -      /* Check for Atmel Ready bit */ -      sflash_cmd (osh, cc, SFLASH_AT_STATUS); -      return !(R_REG (osh, &cc->flashdata) & SFLASH_AT_READY); -    } +	/* Call erase callback */ +	if (erase->callback) +		erase->callback(erase); -  return 0; +	return ret;  } -/* Write len bytes starting at offset into buf. Returns number of bytes - * written. Caller should poll for completion. - */ -int -sflash_write (sb_t * sbh, chipcregs_t * cc, uint offset, uint len, -	      const uchar * buf) +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define sflash_mtd_init init_module +#define sflash_mtd_exit cleanup_module +#endif + +mod_init_t +sflash_mtd_init(void)  { -  struct sflash *sfl; -  int ret = 0; -  bool is4712b0; -  uint32 page, byte, mask; -  osl_t *osh; - -  ASSERT (sbh); - -  osh = sb_osh (sbh); - -  if (!len) -    return 0; - -  if ((offset + len) > sflash.size) -    return -22; - -  sfl = &sflash; -  switch (sfl->type) -    { -    case SFLASH_ST: -      is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3); -      /* Enable writes */ -      sflash_cmd (osh, cc, SFLASH_ST_WREN); -      if (is4712b0) -	{ -	  mask = 1 << 14; -	  W_REG (osh, &cc->flashaddress, offset); -	  W_REG (osh, &cc->flashdata, *buf++); -	  /* Set chip select */ -	  OR_REG (osh, &cc->gpioout, mask); -	  /* Issue a page program with the first byte */ -	  sflash_cmd (osh, cc, SFLASH_ST_PP); -	  ret = 1; -	  offset++; -	  len--; -	  while (len > 0) -	    { -	      if ((offset & 255) == 0) -		{ -		  /* Page boundary, drop cs and return */ -		  AND_REG (osh, &cc->gpioout, ~mask); -		  if (!sflash_poll (sbh, cc, offset)) -		    { -		      /* Flash rejected command */ -		      return -11; -		    } -		  return ret; -		} -	      else -		{ -		  /* Write single byte */ -		  sflash_cmd (osh, cc, *buf++); -		} -	      ret++; -	      offset++; -	      len--; -	    } -	  /* All done, drop cs if needed */ -	  if ((offset & 255) != 1) -	    { -	      /* Drop cs */ -	      AND_REG (osh, &cc->gpioout, ~mask); -	      if (!sflash_poll (sbh, cc, offset)) -		{ -		  /* Flash rejected command */ -		  return -12; -		} -	    } -	} -      else if (sbh->ccrev >= 20) -	{ -	  W_REG (NULL, &cc->flashaddress, offset); -	  W_REG (NULL, &cc->flashdata, *buf++); -	  /* Issue a page program with CSA bit set */ -	  sflash_cmd (osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); -	  ret = 1; -	  offset++; -	  len--; -	  while (len > 0) -	    { -	      if ((offset & 255) == 0) -		{ -		  /* Page boundary, poll droping cs and return */ -		  W_REG (NULL, &cc->flashcontrol, 0); -		  if (!sflash_poll (sbh, cc, offset)) -		    { -		      /* Flash rejected command */ -		      return -11; -		    } -		  return ret; -		} -	      else -		{ -		  /* Write single byte */ -		  sflash_cmd (osh, cc, SFLASH_ST_CSA | *buf++); -		} -	      ret++; -	      offset++; -	      len--; -	    } -	  /* All done, drop cs if needed */ -	  if ((offset & 255) != 1) -	    { -	      /* Drop cs, poll */ -	      W_REG (NULL, &cc->flashcontrol, 0); -	      if (!sflash_poll (sbh, cc, offset)) -		{ -		  /* Flash rejected command */ -		  return -12; -		} -	    } -	} -      else -	{ -	  ret = 1; -	  W_REG (osh, &cc->flashaddress, offset); -	  W_REG (osh, &cc->flashdata, *buf); -	  /* Page program */ -	  sflash_cmd (osh, cc, SFLASH_ST_PP); -	} -      break; -    case SFLASH_AT: -      mask = sfl->blocksize - 1; -      page = (offset & ~mask) << 1; -      byte = offset & mask; -      /* Read main memory page into buffer 1 */ -      if (byte || (len < sfl->blocksize)) -	{ -	  W_REG (osh, &cc->flashaddress, page); -	  sflash_cmd (osh, cc, SFLASH_AT_BUF1_LOAD); -	  /* 250 us for AT45DB321B */ -	  SPINWAIT (sflash_poll (sbh, cc, offset), 1000); -	  ASSERT (!sflash_poll (sbh, cc, offset)); +	struct pci_dev *pdev; +	int ret = 0; +	struct sflash *info; +	uint i; +#ifdef CONFIG_MTD_PARTITIONS +	struct mtd_partition *parts; +#endif + +	if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) { +		printk(KERN_ERR "sflash: chipcommon not found\n"); +		return -ENODEV;  	} -      /* Write into buffer 1 */ -      for (ret = 0; (ret < (int) len) && (byte < sfl->blocksize); ret++) -	{ -	  W_REG (osh, &cc->flashaddress, byte++); -	  W_REG (osh, &cc->flashdata, *buf++); -	  sflash_cmd (osh, cc, SFLASH_AT_BUF1_WRITE); -	} -      /* Write buffer 1 into main memory page */ -      W_REG (osh, &cc->flashaddress, page); -      sflash_cmd (osh, cc, SFLASH_AT_BUF1_PROGRAM); -      break; -    } - -  return ret; -} -/* Erase a region. Returns number of bytes scheduled for erasure. - * Caller should poll for completion. - */ -int -sflash_erase (sb_t * sbh, chipcregs_t * cc, uint offset) -{ -  struct sflash *sfl; -  osl_t *osh; - -  ASSERT (sbh); - -  osh = sb_osh (sbh); - -  if (offset >= sflash.size) -    return -22; - -  sfl = &sflash; -  switch (sfl->type) -    { -    case SFLASH_ST: -      sflash_cmd (osh, cc, SFLASH_ST_WREN); -      W_REG (osh, &cc->flashaddress, offset); -      sflash_cmd (osh, cc, SFLASH_ST_SE); -      return sfl->blocksize; -    case SFLASH_AT: -      W_REG (osh, &cc->flashaddress, offset << 1); -      sflash_cmd (osh, cc, SFLASH_AT_PAGE_ERASE); -      return sfl->blocksize; -    } - -  return 0; -} +	memset(&sflash, 0, sizeof(struct sflash_mtd)); +	init_MUTEX(&sflash.lock); -/* - * writes the appropriate range of flash, a NULL buf simply erases - * the region of flash - */ -int -sflash_commit (sb_t * sbh, chipcregs_t * cc, uint offset, uint len, -	       const uchar * buf) -{ -  struct sflash *sfl; -  uchar *block = NULL, *cur_ptr, *blk_ptr; -  uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; -  uint blk_offset, blk_len, copied; -  int bytes, ret = 0; -  osl_t *osh; - -  ASSERT (sbh); - -  osh = sb_osh (sbh); - -  /* Check address range */ -  if (len <= 0) -    return 0; - -  sfl = &sflash; -  if ((offset + len) > sfl->size) -    return -1; - -  blocksize = sfl->blocksize; -  mask = blocksize - 1; - -  /* Allocate a block of mem */ -  if (!(block = MALLOC (osh, blocksize))) -    return -1; - -  while (len) -    { -      /* Align offset */ -      cur_offset = offset & ~mask; -      cur_length = blocksize; -      cur_ptr = block; - -      remainder = blocksize - (offset & mask); -      if (len < remainder) -	cur_retlen = len; -      else -	cur_retlen = remainder; - -      /* buf == NULL means erase only */ -      if (buf) -	{ -	  /* Copy existing data into holding block if necessary */ -	  if ((offset & mask) || (len < blocksize)) -	    { -	      blk_offset = cur_offset; -	      blk_len = cur_length; -	      blk_ptr = cur_ptr; - -	      /* Copy entire block */ -	      while (blk_len) -		{ -		  copied = -		    sflash_read (sbh, cc, blk_offset, blk_len, blk_ptr); -		  blk_offset += copied; -		  blk_len -= copied; -		  blk_ptr += copied; -		} -	    } +	/* attach to the backplane */ +	if (!(sflash.sbh = sb_kattach(SB_OSH))) { +		printk(KERN_ERR "sflash: error attaching to backplane\n"); +		ret = -EIO; +		goto fail; +	} -	  /* Copy input data into holding block */ -	  memcpy (cur_ptr + (offset & mask), buf, cur_retlen); +	/* Map registers and flash base */ +	if (!(sflash.cc = ioremap_nocache(pci_resource_start(pdev, 0), +					  pci_resource_len(pdev, 0)))) { +		printk(KERN_ERR "sflash: error mapping registers\n"); +		ret = -EIO; +		goto fail;  	} -      /* Erase block */ -      if ((ret = sflash_erase (sbh, cc, (uint) cur_offset)) < 0) -	goto done; -      while (sflash_poll (sbh, cc, (uint) cur_offset)); - -      /* buf == NULL means erase only */ -      if (!buf) -	{ -	  offset += cur_retlen; -	  len -= cur_retlen; -	  continue; +	/* Initialize serial flash access */ +	if (!(info = sflash_init(sflash.sbh, sflash.cc))) { +		printk(KERN_ERR "sflash: found no supported devices\n"); +		ret = -ENODEV; +		goto fail;  	} -      /* Write holding block */ -      while (cur_length > 0) -	{ -	  if ((bytes = sflash_write (sbh, cc, -				     (uint) cur_offset, -				     (uint) cur_length, -				     (uchar *) cur_ptr)) < 0) -	    { -	      ret = bytes; -	      goto done; -	    } -	  while (sflash_poll (sbh, cc, (uint) cur_offset)); -	  cur_offset += bytes; -	  cur_length -= bytes; -	  cur_ptr += bytes; +	printk(KERN_INFO "sflash: found serial flash; blocksize=%dKB, numblocks=%d, size=%dKB\n",info->blocksize/1024,info->numblocks,info->size/1024); + +	/* Setup region info */ +	sflash.region.offset = 0; +	sflash.region.erasesize = info->blocksize; +	sflash.region.numblocks = info->numblocks; +	if (sflash.region.erasesize > sflash.mtd.erasesize) +		sflash.mtd.erasesize = sflash.region.erasesize; +	sflash.mtd.size = info->size; +	sflash.mtd.numeraseregions = 1; + +	/* Register with MTD */ +	sflash.mtd.name = "sflash"; +	sflash.mtd.type = MTD_NORFLASH; +	sflash.mtd.flags = MTD_CAP_NORFLASH; +	sflash.mtd.eraseregions = &sflash.region; +	sflash.mtd.module = THIS_MODULE; +	sflash.mtd.erase = sflash_mtd_erase; +	sflash.mtd.read = sflash_mtd_read; +	sflash.mtd.write = sflash_mtd_write; +	sflash.mtd.priv = &sflash; + +#ifdef CONFIG_MTD_PARTITIONS +	parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size); +	for (i = 0; parts[i].name; i++); +	ret = add_mtd_partitions(&sflash.mtd, parts, i); +#else +	ret = add_mtd_device(&sflash.mtd); +#endif +	if (ret) { +		printk(KERN_ERR "sflash: add_mtd failed\n"); +		goto fail;  	} -      offset += cur_retlen; -      len -= cur_retlen; -      buf += cur_retlen; -    } +	return 0; + + fail: +	if (sflash.cc) +		iounmap((void *) sflash.cc); +	if (sflash.sbh) +		sb_detach(sflash.sbh); +	return ret; +} -  ret = len; -done: -  if (block) -    MFREE (osh, block, blocksize); -  return ret; +mod_exit_t +sflash_mtd_exit(void) +{ +#ifdef CONFIG_MTD_PARTITIONS +	del_mtd_partitions(&sflash.mtd); +#else +	del_mtd_device(&sflash.mtd); +#endif +	iounmap((void *) sflash.cc); +	sb_detach(sflash.sbh);  } + +module_init(sflash_mtd_init); +module_exit(sflash_mtd_exit); diff --git a/target/linux/brcm-2.4/patches/004-flash.patch b/target/linux/brcm-2.4/patches/004-flash.patch index f34efb014..7de8a82eb 100644 --- a/target/linux/brcm-2.4/patches/004-flash.patch +++ b/target/linux/brcm-2.4/patches/004-flash.patch @@ -1,3 +1,13 @@ +--- a/arch/mips/bcm947xx/Makefile	 ++++ b/arch/mips/bcm947xx/Makefile	 +@@ -11,6 +11,7 @@ + obj-y		:= prom.o setup.o time.o sbmips.o gpio.o + obj-y		+= nvram.o nvram_linux.o cfe_env.o hndpmu.o + obj-y		+= sbutils.o utils.o bcmsrom.o hndchipc.o ++obj-y		+= sflash.o + obj-$(CONFIG_PCI) += sbpci.o pcibios.o + obj-y 		+= export.o +   --- a/drivers/mtd/devices/Config.in  +++ b/drivers/mtd/devices/Config.in  @@ -5,6 +5,7 @@ | 
