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