1 /* 2 * (C) Copyright 2012 Stephen Warren 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <asm/io.h> 9 #include <asm/arch/mbox.h> 10 #include <phys2bus.h> 11 12 #define TIMEOUT 1000 /* ms */ 13 14 int bcm2835_mbox_call_raw(u32 chan, u32 send, u32 *recv) 15 { 16 struct bcm2835_mbox_regs *regs = 17 (struct bcm2835_mbox_regs *)BCM2835_MBOX_PHYSADDR; 18 ulong endtime = get_timer(0) + TIMEOUT; 19 u32 val; 20 21 debug("time: %lu timeout: %lu\n", get_timer(0), endtime); 22 23 if (send & BCM2835_CHAN_MASK) { 24 printf("mbox: Illegal mbox data 0x%08x\n", send); 25 return -1; 26 } 27 28 /* Drain any stale responses */ 29 30 for (;;) { 31 val = readl(®s->status); 32 if (val & BCM2835_MBOX_STATUS_RD_EMPTY) 33 break; 34 if (get_timer(0) >= endtime) { 35 printf("mbox: Timeout draining stale responses\n"); 36 return -1; 37 } 38 val = readl(®s->read); 39 } 40 41 /* Wait for space to send */ 42 43 for (;;) { 44 val = readl(®s->status); 45 if (!(val & BCM2835_MBOX_STATUS_WR_FULL)) 46 break; 47 if (get_timer(0) >= endtime) { 48 printf("mbox: Timeout waiting for send space\n"); 49 return -1; 50 } 51 } 52 53 /* Send the request */ 54 55 val = BCM2835_MBOX_PACK(chan, send); 56 debug("mbox: TX raw: 0x%08x\n", val); 57 writel(val, ®s->write); 58 59 /* Wait for the response */ 60 61 for (;;) { 62 val = readl(®s->status); 63 if (!(val & BCM2835_MBOX_STATUS_RD_EMPTY)) 64 break; 65 if (get_timer(0) >= endtime) { 66 printf("mbox: Timeout waiting for response\n"); 67 return -1; 68 } 69 } 70 71 /* Read the response */ 72 73 val = readl(®s->read); 74 debug("mbox: RX raw: 0x%08x\n", val); 75 76 /* Validate the response */ 77 78 if (BCM2835_MBOX_UNPACK_CHAN(val) != chan) { 79 printf("mbox: Response channel mismatch\n"); 80 return -1; 81 } 82 83 *recv = BCM2835_MBOX_UNPACK_DATA(val); 84 85 return 0; 86 } 87 88 #ifdef DEBUG 89 void dump_buf(struct bcm2835_mbox_hdr *buffer) 90 { 91 u32 *p; 92 u32 words; 93 int i; 94 95 p = (u32 *)buffer; 96 words = buffer->buf_size / 4; 97 for (i = 0; i < words; i++) 98 printf(" 0x%04x: 0x%08x\n", i * 4, p[i]); 99 } 100 #endif 101 102 int bcm2835_mbox_call_prop(u32 chan, struct bcm2835_mbox_hdr *buffer) 103 { 104 int ret; 105 u32 rbuffer; 106 struct bcm2835_mbox_tag_hdr *tag; 107 int tag_index; 108 109 #ifdef DEBUG 110 printf("mbox: TX buffer\n"); 111 dump_buf(buffer); 112 #endif 113 114 ret = bcm2835_mbox_call_raw(chan, phys_to_bus((u32)buffer), &rbuffer); 115 if (ret) 116 return ret; 117 if (rbuffer != phys_to_bus((u32)buffer)) { 118 printf("mbox: Response buffer mismatch\n"); 119 return -1; 120 } 121 122 #ifdef DEBUG 123 printf("mbox: RX buffer\n"); 124 dump_buf(buffer); 125 #endif 126 127 /* Validate overall response status */ 128 129 if (buffer->code != BCM2835_MBOX_RESP_CODE_SUCCESS) { 130 printf("mbox: Header response code invalid\n"); 131 return -1; 132 } 133 134 /* Validate each tag's response status */ 135 136 tag = (void *)(buffer + 1); 137 tag_index = 0; 138 while (tag->tag) { 139 if (!(tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE)) { 140 printf("mbox: Tag %d missing val_len response bit\n", 141 tag_index); 142 return -1; 143 } 144 /* 145 * Clear the reponse bit so clients can just look right at the 146 * length field without extra processing 147 */ 148 tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; 149 tag = (void *)(((u8 *)tag) + sizeof(*tag) + tag->val_buf_size); 150 tag_index++; 151 } 152 153 return 0; 154 } 155