blob: c6934e684c5f3a8bfc88f282551752a4d70bf9dc [file] [log] [blame]
Andrew Jeffery1e531af2018-08-07 13:32:57 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3#include "config.h"
4
5#include <errno.h>
6#include <stdint.h>
7
Andrew Jeffery5335f092018-08-09 14:56:08 +09308#include "common.h"
Andrew Jefferyc48d0702018-08-09 14:04:27 +09309#include "flash.h"
Andrew Jeffery26558db2018-08-10 00:22:38 +093010#include "mboxd.h"
Andrew Jeffery1e531af2018-08-07 13:32:57 +093011#include "lpc.h"
Andrew Jeffery1e531af2018-08-07 13:32:57 +093012#include "windows.h"
13
Andrew Jeffery26558db2018-08-10 00:22:38 +093014#define BLOCK_SIZE_SHIFT_V1 12 /* 4K */
15
Andrew Jeffery5335f092018-08-09 14:56:08 +093016/*
17 * protocol_events_set() - Set BMC events
18 * @context: The mbox context pointer
19 * @bmc_event: The bits to set
20 * @write_back: Whether to write back to the register -> will interrupt host
21 *
22 * Return: 0 on success otherwise negative error code
23 */
24int protocol_events_set(struct mbox_context *context, uint8_t bmc_event,
25 bool write_back)
26{
27 uint8_t mask = 0x00;
28
29 switch (context->version) {
30 case API_VERSION_1:
31 mask = BMC_EVENT_V1_MASK;
32 break;
33 default:
34 mask = BMC_EVENT_V2_MASK;
35 break;
36 }
37
38 context->bmc_events |= (bmc_event & mask);
39 MSG_DBG("BMC Events set to: 0x%.2x\n", context->bmc_events);
40
41 return write_back ? context->transport->flush_events(context) : 0;
42}
43
44/*
45 * protocol_events_clear() - Clear BMC events
46 * @context: The mbox context pointer
47 * @bmc_event: The bits to clear
48 * @write_back: Whether to write back to the register -> will interrupt host
49 *
50 * Return: 0 on success otherwise negative error code
51 */
52int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event,
53 bool write_back)
54{
55 context->bmc_events &= ~bmc_event;
56 MSG_DBG("BMC Events clear to: 0x%.2x\n", context->bmc_events);
57
58 return write_back ? context->transport->flush_events(context) : 0;
59}
60
Andrew Jefferyab666a52018-08-07 14:28:09 +093061int protocol_v1_reset(struct mbox_context *context)
62{
63 /* Host requested it -> No BMC Event */
Andrew Jeffery5335f092018-08-09 14:56:08 +093064 windows_reset_all(context, EVENT_SUPPRESS);
Andrew Jefferyab666a52018-08-07 14:28:09 +093065 return lpc_reset(context);
66}
67
Andrew Jeffery1e531af2018-08-07 13:32:57 +093068int protocol_v1_get_info(struct mbox_context *context,
69 struct protocol_get_info *io)
70{
71 uint8_t old_version = context->version;
72 int rc;
73
74 /* Bootstrap protocol version. This may involve {up,down}grading */
75 rc = protocol_negotiate_version(context, io->req.api_version);
76 if (rc < 0)
77 return rc;
78
79 /* Do the {up,down}grade if necessary*/
80 if (rc != old_version) {
Andrew Jeffery5335f092018-08-09 14:56:08 +093081 windows_reset_all(context, EVENT_TRIGGER);
Andrew Jeffery1e531af2018-08-07 13:32:57 +093082 return context->protocol->get_info(context, io);
83 }
84
85 /* Record the negotiated version for the response */
86 io->resp.api_version = rc;
87
88 /* Now do all required intialisation for v1 */
89 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
90 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
91 1 << context->block_size_shift, context->block_size_shift);
92
93 /* Knowing blocksize we can allocate the window dirty_bytemap */
94 windows_alloc_dirty_bytemap(context);
95
96 io->resp.v1.read_window_size =
97 context->windows.default_size >> context->block_size_shift;
98 io->resp.v1.write_window_size =
99 context->windows.default_size >> context->block_size_shift;
100
101 return lpc_map_memory(context);
102}
103
Andrew Jeffery91a87452018-08-07 14:54:14 +0930104int protocol_v1_get_flash_info(struct mbox_context *context,
105 struct protocol_get_flash_info *io)
106{
107 io->resp.v1.flash_size = context->flash_size;
108 io->resp.v1.erase_size = context->mtd_info.erasesize;
109
110 return 0;
111}
112
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930113/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930114 * get_lpc_addr_shifted() - Get lpc address of the current window
115 * @context: The mbox context pointer
116 *
117 * Return: The lpc address to access that offset shifted by block size
118 */
119static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
120{
121 uint32_t lpc_addr, mem_offset;
122
123 /* Offset of the current window in the reserved memory region */
124 mem_offset = context->current->mem - context->mem;
125 /* Total LPC Address */
126 lpc_addr = context->lpc_base + mem_offset;
127
128 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
129
130 return lpc_addr >> context->block_size_shift;
131}
132
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930133int protocol_v1_create_window(struct mbox_context *context,
134 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930135{
136 int rc;
137 uint32_t offset = io->req.offset << context->block_size_shift;
138
139 /* Close the current window if there is one */
140 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930141 /* There is an implicit flush if it was a write window
142 *
143 * protocol_v2_create_window() calls
144 * protocol_v1_create_window(), so use indirect call to
145 * write_flush() to make sure we pick the right one.
146 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930147 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930148 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930149 if (rc < 0) {
150 MSG_ERR("Couldn't Flush Write Window\n");
151 return rc;
152 }
153 }
Andrew Jeffery5335f092018-08-09 14:56:08 +0930154 windows_close_current(context, EVENT_SUPPRESS, FLAGS_NONE);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930155 }
156
157 /* Offset the host has requested */
158 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
159 /* Check if we have an existing window */
160 context->current = windows_search(context, offset,
161 context->version == API_VERSION_1);
162
163 if (!context->current) { /* No existing window */
164 MSG_DBG("No existing window which maps that flash offset\n");
165 rc = windows_create_map(context, &context->current,
166 offset,
167 context->version == API_VERSION_1);
168 if (rc < 0) { /* Unable to map offset */
169 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930170 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930171 return rc;
172 }
173 }
174
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930175 context->current_is_write = !io->req.ro;
176
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930177 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
178 context->current->mem, context->current->size,
179 context->current->flash_offset);
180
181 io->resp.lpc_address = get_lpc_addr_shifted(context);
182
183 return 0;
184}
185
Andrew Jefferya336e432018-08-07 16:00:40 +0930186int protocol_v1_mark_dirty(struct mbox_context *context,
187 struct protocol_mark_dirty *io)
188{
189 uint32_t offset = io->req.v1.offset;
190 uint32_t size = io->req.v1.size;
191 uint32_t off;
192
193 if (!(context->current && context->current_is_write)) {
194 MSG_ERR("Tried to call mark dirty without open write window\n");
195 return -EPERM;
196 }
197
198 /* For V1 offset given relative to flash - we want the window */
199 off = offset - ((context->current->flash_offset) >>
200 context->block_size_shift);
201 if (off > offset) { /* Underflow - before current window */
202 MSG_ERR("Tried to mark dirty before start of window\n");
203 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
204 offset << context->block_size_shift,
205 context->current->flash_offset);
206 return -EINVAL;
207 }
208 offset = off;
209 /*
210 * We only track dirty at the block level.
211 * For protocol V1 we can get away with just marking the whole
212 * block dirty.
213 */
214 size = align_up(size, 1 << context->block_size_shift);
215 size >>= context->block_size_shift;
216
217 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
218 offset << context->block_size_shift,
219 size << context->block_size_shift);
220
221 return window_set_bytemap(context, context->current, offset, size,
222 WINDOW_DIRTY);
223}
224
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930225static int generic_flush(struct mbox_context *context)
226{
227 int rc, i, offset, count;
228 uint8_t prev;
229
230 offset = 0;
231 count = 0;
232 prev = WINDOW_CLEAN;
233
234 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
235 context->current->mem, context->current->size,
236 context->current->flash_offset);
237
238 /*
239 * We look for streaks of the same type and keep a count, when the type
240 * (dirty/erased) changes we perform the required action on the backing
241 * store and update the current streak-type
242 */
243 for (i = 0; i < (context->current->size >> context->block_size_shift);
244 i++) {
245 uint8_t cur = context->current->dirty_bmap[i];
246 if (cur != WINDOW_CLEAN) {
247 if (cur == prev) { /* Same as previous block, incrmnt */
248 count++;
249 } else if (prev == WINDOW_CLEAN) { /* Start of run */
250 offset = i;
251 count++;
252 } else { /* Change in streak type */
253 rc = window_flush(context, offset, count,
254 prev);
255 if (rc < 0) {
256 return rc;
257 }
258 offset = i;
259 count = 1;
260 }
261 } else {
262 if (prev != WINDOW_CLEAN) { /* End of a streak */
263 rc = window_flush(context, offset, count,
264 prev);
265 if (rc < 0) {
266 return rc;
267 }
268 offset = 0;
269 count = 0;
270 }
271 }
272 prev = cur;
273 }
274
275 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
276 rc = window_flush(context, offset, count, prev);
277 if (rc < 0) {
278 return rc;
279 }
280 }
281
282 /* Clear the dirty bytemap since we have written back all changes */
283 return window_set_bytemap(context, context->current, 0,
284 context->current->size >>
285 context->block_size_shift,
286 WINDOW_CLEAN);
287}
288
289int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
290{
291 int rc;
292
293 if (!(context->current && context->current_is_write)) {
294 MSG_ERR("Tried to call flush without open write window\n");
295 return -EPERM;
296 }
297
298 /*
299 * For V1 the Flush command acts much the same as the dirty command
300 * except with a flush as well. Only do this on an actual flush
301 * command not when we call flush because we've implicitly closed a
302 * window because we might not have the required args in req.
303 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930304 if (io) {
305 struct protocol_mark_dirty *mdio = (void *)io;
306 rc = protocol_v1_mark_dirty(context, mdio);
307 if (rc < 0) {
308 return rc;
309 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930310 }
311
312 return generic_flush(context);
313}
314
Andrew Jeffery093eda52018-08-07 23:10:43 +0930315int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
316{
317 int rc;
318
319 /* Close the current window if there is one */
320 if (!context->current) {
321 return 0;
322 }
323
324 /* There is an implicit flush if it was a write window */
325 if (context->current_is_write) {
326 rc = protocol_v1_flush(context, NULL);
327 if (rc < 0) {
328 MSG_ERR("Couldn't Flush Write Window\n");
329 return rc;
330 }
331 }
332
333 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery5335f092018-08-09 14:56:08 +0930334 windows_close_current(context, EVENT_SUPPRESS, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930335
336 return 0;
337}
338
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930339int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
340{
Andrew Jeffery5335f092018-08-09 14:56:08 +0930341 return protocol_events_clear(context, (io->req.flags & BMC_EVENT_ACK_MASK),
342 EVENT_TRIGGER);
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930343}
344
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930345/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930346 * get_suggested_timeout() - get the suggested timeout value in seconds
347 * @context: The mbox context pointer
348 *
349 * Return: Suggested timeout in seconds
350 */
351static uint16_t get_suggested_timeout(struct mbox_context *context)
352{
353 struct window_context *window = windows_find_largest(context);
354 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
355 uint16_t ret;
356
357 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
358
359 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
360 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
361 return ret;
362}
363
364int protocol_v2_get_info(struct mbox_context *context,
365 struct protocol_get_info *io)
366{
367 uint8_t old_version = context->version;
368 int rc;
369
370 /* Bootstrap protocol version. This may involve {up,down}grading */
371 rc = protocol_negotiate_version(context, io->req.api_version);
372 if (rc < 0)
373 return rc;
374
375 /* Do the {up,down}grade if necessary*/
376 if (rc != old_version) {
Andrew Jeffery5335f092018-08-09 14:56:08 +0930377 windows_reset_all(context, EVENT_TRIGGER);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930378 return context->protocol->get_info(context, io);
379 }
380
381 /* Record the negotiated version for the response */
382 io->resp.api_version = rc;
383
384 /* Now do all required intialisation for v2 */
385 context->block_size_shift = log_2(context->mtd_info.erasesize);
386 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
387 1 << context->block_size_shift, context->block_size_shift);
388
389 /* Knowing blocksize we can allocate the window dirty_bytemap */
390 windows_alloc_dirty_bytemap(context);
391
392 io->resp.v2.block_size_shift = context->block_size_shift;
393 io->resp.v2.timeout = get_suggested_timeout(context);
394
395 return lpc_map_memory(context);
396}
397
Andrew Jeffery91a87452018-08-07 14:54:14 +0930398int protocol_v2_get_flash_info(struct mbox_context *context,
399 struct protocol_get_flash_info *io)
400{
401 io->resp.v2.flash_size =
402 context->flash_size >> context->block_size_shift;
403 io->resp.v2.erase_size =
404 context->mtd_info.erasesize >> context->block_size_shift;
405
406 return 0;
407}
408
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930409int protocol_v2_create_window(struct mbox_context *context,
410 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930411{
412 int rc;
413
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930414 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930415 if (rc < 0)
416 return rc;
417
418 io->resp.size = context->current->size >> context->block_size_shift;
419 io->resp.offset = context->current->flash_offset >>
420 context->block_size_shift;
421
422 return 0;
423}
424
Andrew Jefferya336e432018-08-07 16:00:40 +0930425int protocol_v2_mark_dirty(struct mbox_context *context,
426 struct protocol_mark_dirty *io)
427{
428 if (!(context->current && context->current_is_write)) {
429 MSG_ERR("Tried to call mark dirty without open write window\n");
430 return -EPERM;
431 }
432
433 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
434 io->req.v2.offset << context->block_size_shift,
435 io->req.v2.size << context->block_size_shift);
436
437 return window_set_bytemap(context, context->current, io->req.v2.offset,
438 io->req.v2.size, WINDOW_DIRTY);
439}
440
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930441int protocol_v2_erase(struct mbox_context *context,
442 struct protocol_erase *io)
443{
444 size_t start, len;
445 int rc;
446
447 if (!(context->current && context->current_is_write)) {
448 MSG_ERR("Tried to call erase without open write window\n");
449 return -EPERM;
450 }
451
452 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
453 io->req.offset << context->block_size_shift,
454 io->req.size << context->block_size_shift);
455
456 rc = window_set_bytemap(context, context->current, io->req.offset,
457 io->req.size, WINDOW_ERASED);
458 if (rc < 0) {
459 return rc;
460 }
461
462 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
463 start = io->req.offset << context->block_size_shift;
464 len = io->req.size << context->block_size_shift;
465 memset(context->current->mem + start, 0xFF, len);
466
467 return 0;
468}
469
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930470int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
471{
472 if (!(context->current && context->current_is_write)) {
473 MSG_ERR("Tried to call flush without open write window\n");
474 return -EPERM;
475 }
476
477 return generic_flush(context);
478}
479
Andrew Jeffery093eda52018-08-07 23:10:43 +0930480int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
481{
482 int rc;
483
484 /* Close the current window if there is one */
485 if (!context->current) {
486 return 0;
487 }
488
489 /* There is an implicit flush if it was a write window */
490 if (context->current_is_write) {
491 rc = protocol_v2_flush(context, NULL);
492 if (rc < 0) {
493 MSG_ERR("Couldn't Flush Write Window\n");
494 return rc;
495 }
496 }
497
498 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery5335f092018-08-09 14:56:08 +0930499 windows_close_current(context, EVENT_SUPPRESS, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930500
501 return 0;
502}
503
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930504int protocol_init(struct mbox_context *context)
505{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930506 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930507
508 return 0;
509}
510
511void protocol_free(struct mbox_context *context)
512{
513 return;
514}