1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2012 Stephen Warren
4  */
5 
6 #include <common.h>
7 #include <memalign.h>
8 #include <phys2bus.h>
9 #include <asm/arch/mbox.h>
10 #include <linux/delay.h>
11 
12 struct msg_set_power_state {
13 	struct bcm2835_mbox_hdr hdr;
14 	struct bcm2835_mbox_tag_set_power_state set_power_state;
15 	u32 end_tag;
16 };
17 
18 struct msg_get_clock_rate {
19 	struct bcm2835_mbox_hdr hdr;
20 	struct bcm2835_mbox_tag_get_clock_rate get_clock_rate;
21 	u32 end_tag;
22 };
23 
24 struct msg_set_sdhost_clock {
25 	struct bcm2835_mbox_hdr hdr;
26 	struct bcm2835_mbox_tag_set_sdhost_clock set_sdhost_clock;
27 	u32 end_tag;
28 };
29 
30 struct msg_query {
31 	struct bcm2835_mbox_hdr hdr;
32 	struct bcm2835_mbox_tag_physical_w_h physical_w_h;
33 	u32 end_tag;
34 };
35 
36 struct msg_setup {
37 	struct bcm2835_mbox_hdr hdr;
38 	struct bcm2835_mbox_tag_physical_w_h physical_w_h;
39 	struct bcm2835_mbox_tag_virtual_w_h virtual_w_h;
40 	struct bcm2835_mbox_tag_depth depth;
41 	struct bcm2835_mbox_tag_pixel_order pixel_order;
42 	struct bcm2835_mbox_tag_alpha_mode alpha_mode;
43 	struct bcm2835_mbox_tag_virtual_offset virtual_offset;
44 	struct bcm2835_mbox_tag_overscan overscan;
45 	struct bcm2835_mbox_tag_allocate_buffer allocate_buffer;
46 	struct bcm2835_mbox_tag_pitch pitch;
47 	u32 end_tag;
48 };
49 
50 struct msg_notify_vl805_reset {
51 	struct bcm2835_mbox_hdr hdr;
52 	struct bcm2835_mbox_tag_pci_dev_addr dev_addr;
53 	u32 end_tag;
54 };
55 
bcm2835_power_on_module(u32 module)56 int bcm2835_power_on_module(u32 module)
57 {
58 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_set_power_state, msg_pwr, 1);
59 	int ret;
60 
61 	BCM2835_MBOX_INIT_HDR(msg_pwr);
62 	BCM2835_MBOX_INIT_TAG(&msg_pwr->set_power_state,
63 			      SET_POWER_STATE);
64 	msg_pwr->set_power_state.body.req.device_id = module;
65 	msg_pwr->set_power_state.body.req.state =
66 		BCM2835_MBOX_SET_POWER_STATE_REQ_ON |
67 		BCM2835_MBOX_SET_POWER_STATE_REQ_WAIT;
68 
69 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN,
70 				     &msg_pwr->hdr);
71 	if (ret) {
72 		printf("bcm2835: Could not set module %u power state\n",
73 		       module);
74 		return -EIO;
75 	}
76 
77 	return 0;
78 }
79 
bcm2835_get_mmc_clock(u32 clock_id)80 int bcm2835_get_mmc_clock(u32 clock_id)
81 {
82 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_clock_rate, msg_clk, 1);
83 	int ret;
84 	u32 clock_rate = 0;
85 
86 	ret = bcm2835_power_on_module(BCM2835_MBOX_POWER_DEVID_SDHCI);
87 	if (ret)
88 		return ret;
89 
90 	BCM2835_MBOX_INIT_HDR(msg_clk);
91 	BCM2835_MBOX_INIT_TAG(&msg_clk->get_clock_rate, GET_CLOCK_RATE);
92 	msg_clk->get_clock_rate.body.req.clock_id = clock_id;
93 
94 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_clk->hdr);
95 	if (ret) {
96 		printf("bcm2835: Could not query eMMC clock rate\n");
97 		return -EIO;
98 	}
99 
100 	clock_rate = msg_clk->get_clock_rate.body.resp.rate_hz;
101 
102 	if (clock_rate == 0) {
103 		BCM2835_MBOX_INIT_HDR(msg_clk);
104 		BCM2835_MBOX_INIT_TAG(&msg_clk->get_clock_rate, GET_MAX_CLOCK_RATE);
105 		msg_clk->get_clock_rate.body.req.clock_id = clock_id;
106 
107 		ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_clk->hdr);
108 		if (ret) {
109 			printf("bcm2835: Could not query max eMMC clock rate\n");
110 			return -EIO;
111 		}
112 
113 		clock_rate = msg_clk->get_clock_rate.body.resp.rate_hz;
114 	}
115 
116 	return clock_rate;
117 }
118 
bcm2835_set_sdhost_clock(u32 rate_hz,u32 * rate_1,u32 * rate_2)119 int bcm2835_set_sdhost_clock(u32 rate_hz, u32 *rate_1, u32 *rate_2)
120 {
121 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_set_sdhost_clock, msg_sdhost_clk, 1);
122 	int ret;
123 
124 	BCM2835_MBOX_INIT_HDR(msg_sdhost_clk);
125 	BCM2835_MBOX_INIT_TAG(&msg_sdhost_clk->set_sdhost_clock, SET_SDHOST_CLOCK);
126 
127 	msg_sdhost_clk->set_sdhost_clock.body.req.rate_hz = rate_hz;
128 
129 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_sdhost_clk->hdr);
130 	if (ret) {
131 		printf("bcm2835: Could not query sdhost clock rate\n");
132 		return -EIO;
133 	}
134 
135 	*rate_1 = msg_sdhost_clk->set_sdhost_clock.body.resp.rate_1;
136 	*rate_2 = msg_sdhost_clk->set_sdhost_clock.body.resp.rate_2;
137 
138 	return 0;
139 }
140 
bcm2835_get_video_size(int * widthp,int * heightp)141 int bcm2835_get_video_size(int *widthp, int *heightp)
142 {
143 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_query, msg_query, 1);
144 	int ret;
145 
146 	BCM2835_MBOX_INIT_HDR(msg_query);
147 	BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_query->physical_w_h,
148 				     GET_PHYSICAL_W_H);
149 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_query->hdr);
150 	if (ret) {
151 		printf("bcm2835: Could not query display resolution\n");
152 		return ret;
153 	}
154 	*widthp = msg_query->physical_w_h.body.resp.width;
155 	*heightp = msg_query->physical_w_h.body.resp.height;
156 
157 	return 0;
158 }
159 
bcm2835_set_video_params(int * widthp,int * heightp,int depth_bpp,int pixel_order,int alpha_mode,ulong * fb_basep,ulong * fb_sizep,int * pitchp)160 int bcm2835_set_video_params(int *widthp, int *heightp, int depth_bpp,
161 			     int pixel_order, int alpha_mode, ulong *fb_basep,
162 			     ulong *fb_sizep, int *pitchp)
163 {
164 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_setup, msg_setup, 1);
165 	int ret;
166 
167 	BCM2835_MBOX_INIT_HDR(msg_setup);
168 	BCM2835_MBOX_INIT_TAG(&msg_setup->physical_w_h, SET_PHYSICAL_W_H);
169 	msg_setup->physical_w_h.body.req.width = *widthp;
170 	msg_setup->physical_w_h.body.req.height = *heightp;
171 	BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_w_h, SET_VIRTUAL_W_H);
172 	msg_setup->virtual_w_h.body.req.width = *widthp;
173 	msg_setup->virtual_w_h.body.req.height = *heightp;
174 	BCM2835_MBOX_INIT_TAG(&msg_setup->depth, SET_DEPTH);
175 	msg_setup->depth.body.req.bpp = 32;
176 	BCM2835_MBOX_INIT_TAG(&msg_setup->pixel_order, SET_PIXEL_ORDER);
177 	msg_setup->pixel_order.body.req.order = pixel_order;
178 	BCM2835_MBOX_INIT_TAG(&msg_setup->alpha_mode, SET_ALPHA_MODE);
179 	msg_setup->alpha_mode.body.req.alpha = alpha_mode;
180 	BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_offset, SET_VIRTUAL_OFFSET);
181 	msg_setup->virtual_offset.body.req.x = 0;
182 	msg_setup->virtual_offset.body.req.y = 0;
183 	BCM2835_MBOX_INIT_TAG(&msg_setup->overscan, SET_OVERSCAN);
184 	msg_setup->overscan.body.req.top = 0;
185 	msg_setup->overscan.body.req.bottom = 0;
186 	msg_setup->overscan.body.req.left = 0;
187 	msg_setup->overscan.body.req.right = 0;
188 	BCM2835_MBOX_INIT_TAG(&msg_setup->allocate_buffer, ALLOCATE_BUFFER);
189 	msg_setup->allocate_buffer.body.req.alignment = 0x100;
190 	BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_setup->pitch, GET_PITCH);
191 
192 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_setup->hdr);
193 	if (ret) {
194 		printf("bcm2835: Could not configure display\n");
195 		return ret;
196 	}
197 	*widthp = msg_setup->physical_w_h.body.resp.width;
198 	*heightp = msg_setup->physical_w_h.body.resp.height;
199 	*pitchp = msg_setup->pitch.body.resp.pitch;
200 	*fb_basep = bus_to_phys(
201 			msg_setup->allocate_buffer.body.resp.fb_address);
202 	*fb_sizep = msg_setup->allocate_buffer.body.resp.fb_size;
203 
204 	return 0;
205 }
206 
207 /*
208  * On the Raspberry Pi 4, after a PCI reset, VL805's (the xHCI chip) firmware
209  * may either be loaded directly from an EEPROM or, if not present, by the
210  * SoC's VideoCore. This informs VideoCore that VL805 needs its firmware
211  * loaded.
212  */
bcm2711_notify_vl805_reset(void)213 int bcm2711_notify_vl805_reset(void)
214 {
215 	ALLOC_CACHE_ALIGN_BUFFER(struct msg_notify_vl805_reset,
216 				 msg_notify_vl805_reset, 1);
217 	int ret;
218 	static int done = false;
219 
220 	if (done)
221 		return 0;
222 
223 	done = true;
224 
225 	BCM2835_MBOX_INIT_HDR(msg_notify_vl805_reset);
226 	BCM2835_MBOX_INIT_TAG(&msg_notify_vl805_reset->dev_addr,
227 			      NOTIFY_XHCI_RESET);
228 
229 	/*
230 	 * The pci device address is expected like this:
231 	 *
232 	 *   PCI_BUS << 20 | PCI_SLOT << 15 | PCI_FUNC << 12
233 	 *
234 	 * But since RPi4's PCIe setup is hardwired, we know the address in
235 	 * advance.
236 	 */
237 	msg_notify_vl805_reset->dev_addr.body.req.dev_addr = 0x100000;
238 
239 	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN,
240 				     &msg_notify_vl805_reset->hdr);
241 	if (ret) {
242 		printf("bcm2711: Failed to load vl805's firmware, %d\n", ret);
243 		return -EIO;
244 	}
245 
246 	udelay(200);
247 
248 	return 0;
249 }
250