diff options
Diffstat (limited to 'arch/arm/cpu/arm1176/bcm2835/mbox.c')
| -rw-r--r-- | arch/arm/cpu/arm1176/bcm2835/mbox.c | 164 | 
1 files changed, 164 insertions, 0 deletions
| diff --git a/arch/arm/cpu/arm1176/bcm2835/mbox.c b/arch/arm/cpu/arm1176/bcm2835/mbox.c new file mode 100644 index 000000000..fd65e3387 --- /dev/null +++ b/arch/arm/cpu/arm1176/bcm2835/mbox.c @@ -0,0 +1,164 @@ +/* + * (C) Copyright 2012 Stephen Warren + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/mbox.h> + +#define TIMEOUT (100 * 1000) /* 100mS in uS */ + +int bcm2835_mbox_call_raw(u32 chan, u32 send, u32 *recv) +{ +	struct bcm2835_mbox_regs *regs = +		(struct bcm2835_mbox_regs *)BCM2835_MBOX_PHYSADDR; +	ulong endtime = get_timer(0) + TIMEOUT; +	u32 val; + +	debug("time: %lu timeout: %lu\n", get_timer(0), endtime); + +	if (send & BCM2835_CHAN_MASK) { +		printf("mbox: Illegal mbox data 0x%08x\n", send); +		return -1; +	} + +	/* Drain any stale responses */ + +	for (;;) { +		val = readl(®s->status); +		if (val & BCM2835_MBOX_STATUS_RD_EMPTY) +			break; +		if (get_timer(0) >= endtime) { +			printf("mbox: Timeout draining stale responses\n"); +			return -1; +		} +		val = readl(®s->read); +	} + +	/* Wait for space to send */ + +	for (;;) { +		val = readl(®s->status); +		if (!(val & BCM2835_MBOX_STATUS_WR_FULL)) +			break; +		if (get_timer(0) >= endtime) { +			printf("mbox: Timeout waiting for send space\n"); +			return -1; +		} +	} + +	/* Send the request */ + +	val = BCM2835_MBOX_PACK(chan, send); +	debug("mbox: TX raw: 0x%08x\n", val); +	writel(val, ®s->write); + +	/* Wait for the response */ + +	for (;;) { +		val = readl(®s->status); +		if (!(val & BCM2835_MBOX_STATUS_RD_EMPTY)) +			break; +		if (get_timer(0) >= endtime) { +			printf("mbox: Timeout waiting for response\n"); +			return -1; +		} +	} + +	/* Read the response */ + +	val = readl(®s->read); +	debug("mbox: RX raw: 0x%08x\n", val); + +	/* Validate the response */ + +	if (BCM2835_MBOX_UNPACK_CHAN(val) != chan) { +		printf("mbox: Response channel mismatch\n"); +		return -1; +	} + +	*recv = BCM2835_MBOX_UNPACK_DATA(val); + +	return 0; +} + +#ifdef DEBUG +void dump_buf(struct bcm2835_mbox_hdr *buffer) +{ +	u32 *p; +	u32 words; +	int i; + +	p = (u32 *)buffer; +	words = buffer->buf_size / 4; +	for (i = 0; i < words; i++) +		printf("    0x%04x: 0x%08x\n", i * 4, p[i]); +} +#endif + +int bcm2835_mbox_call_prop(u32 chan, struct bcm2835_mbox_hdr *buffer) +{ +	int ret; +	u32 rbuffer; +	struct bcm2835_mbox_tag_hdr *tag; +	int tag_index; + +#ifdef DEBUG +	printf("mbox: TX buffer\n"); +	dump_buf(buffer); +#endif + +	ret = bcm2835_mbox_call_raw(chan, (u32)buffer, &rbuffer); +	if (ret) +		return ret; +	if (rbuffer != (u32)buffer) { +		printf("mbox: Response buffer mismatch\n"); +		return -1; +	} + +#ifdef DEBUG +	printf("mbox: RX buffer\n"); +	dump_buf(buffer); +#endif + +	/* Validate overall response status */ + +	if (buffer->code != BCM2835_MBOX_RESP_CODE_SUCCESS) { +		printf("mbox: Header response code invalid\n"); +		return -1; +	} + +	/* Validate each tag's response status */ + +	tag = (void *)(buffer + 1); +	tag_index = 0; +	while (tag->tag) { +		if (!(tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE)) { +			printf("mbox: Tag %d missing val_len response bit\n", +				tag_index); +			return -1; +		} +		/* +		 * Clear the reponse bit so clients can just look right at the +		 * length field without extra processing +		 */ +		tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; +		tag = (void *)(((u8 *)tag) + sizeof(*tag) + tag->val_buf_size); +		tag_index++; +	} + +	return 0; +} |