uboot/arch/arm/mach-socfpga/mailbox_s10.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2017-2018 Intel Corporation <www.intel.com>
   4 *
   5 */
   6
   7#include <common.h>
   8#include <hang.h>
   9#include <wait_bit.h>
  10#include <asm/io.h>
  11#include <asm/arch/mailbox_s10.h>
  12#include <asm/arch/system_manager.h>
  13#include <asm/secure.h>
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17#define MBOX_READL(reg)                 \
  18         readl(SOCFPGA_MAILBOX_ADDRESS + (reg))
  19
  20#define MBOX_WRITEL(data, reg)          \
  21        writel(data, SOCFPGA_MAILBOX_ADDRESS + (reg))
  22
  23#define MBOX_READ_RESP_BUF(rout)        \
  24        MBOX_READL(MBOX_RESP_BUF + ((rout) * sizeof(u32)))
  25
  26#define MBOX_WRITE_CMD_BUF(data, cin)   \
  27        MBOX_WRITEL(data, MBOX_CMD_BUF + ((cin) * sizeof(u32)))
  28
  29static __always_inline int mbox_polling_resp(u32 rout)
  30{
  31        u32 rin;
  32        unsigned long i = ~0;
  33
  34        while (i) {
  35                rin = MBOX_READL(MBOX_RIN);
  36                if (rout != rin)
  37                        return 0;
  38
  39                i--;
  40        }
  41
  42        return -ETIMEDOUT;
  43}
  44
  45/* Check for available slot and write to circular buffer.
  46 * It also update command valid offset (cin) register.
  47 */
  48static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
  49                                                       u32 *arg)
  50{
  51        u32 cin;
  52        u32 cout;
  53        u32 i;
  54
  55        cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
  56        cout = MBOX_READL(MBOX_COUT) % MBOX_CMD_BUFFER_SIZE;
  57
  58        /* if command buffer is full or not enough free space
  59         * to fit the data. Note, len is in u32 unit.
  60         */
  61        if (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == cout ||
  62            ((MBOX_CMD_BUFFER_SIZE - cin + cout - 1) %
  63             MBOX_CMD_BUFFER_SIZE) < (len + 1))
  64                return -ENOMEM;
  65
  66        /* write header to circular buffer */
  67        MBOX_WRITE_CMD_BUF(header, cin++);
  68        /* wrapping around when it reach the buffer size */
  69        cin %= MBOX_CMD_BUFFER_SIZE;
  70
  71        /* write arguments */
  72        for (i = 0; i < len; i++) {
  73                MBOX_WRITE_CMD_BUF(arg[i], cin++);
  74                /* wrapping around when it reach the buffer size */
  75                cin %= MBOX_CMD_BUFFER_SIZE;
  76        }
  77
  78        /* write command valid offset */
  79        MBOX_WRITEL(cin, MBOX_CIN);
  80
  81        return 0;
  82}
  83
  84/* Check the command and fill it into circular buffer */
  85static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
  86                                                 u8 is_indirect, u32 len,
  87                                                 u32 *arg)
  88{
  89        u32 header;
  90        int ret;
  91
  92        /* Total length is command + argument length */
  93        if ((len + 1) > MBOX_CMD_BUFFER_SIZE)
  94                return -EINVAL;
  95
  96        if (cmd > MBOX_MAX_CMD_INDEX)
  97                return -EINVAL;
  98
  99        header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
 100                                 (is_indirect) ? 1 : 0, cmd);
 101
 102        ret = mbox_fill_cmd_circular_buff(header, len, arg);
 103
 104        return ret;
 105}
 106
 107/* Send command only without waiting for responses from SDM */
 108static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
 109                                                     u8 is_indirect, u32 len,
 110                                                     u32 *arg)
 111{
 112        int ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
 113        /* write doorbell */
 114        MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
 115
 116        return ret;
 117}
 118
 119/* Return number of responses received in buffer */
 120static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
 121{
 122        u32 rin;
 123        u32 rout;
 124        u32 resp_len = 0;
 125
 126        /* clear doorbell from SDM if it was SET */
 127        if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
 128                MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
 129
 130        /* read current response offset */
 131        rout = MBOX_READL(MBOX_ROUT);
 132        /* read response valid offset */
 133        rin = MBOX_READL(MBOX_RIN);
 134
 135        while (rin != rout && (resp_len < resp_buf_max_len)) {
 136                /* Response received */
 137                if (resp_buf)
 138                        resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
 139
 140                rout++;
 141                /* wrapping around when it reach the buffer size */
 142                rout %= MBOX_RESP_BUFFER_SIZE;
 143                /* update next ROUT */
 144                MBOX_WRITEL(rout, MBOX_ROUT);
 145        }
 146
 147        return resp_len;
 148}
 149
 150/* Support one command and up to 31 words argument length only */
 151static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
 152                                                u32 len, u32 *arg, u8 urgent,
 153                                                u32 *resp_buf_len,
 154                                                u32 *resp_buf)
 155{
 156        u32 rin;
 157        u32 resp;
 158        u32 rout;
 159        u32 status;
 160        u32 resp_len;
 161        u32 buf_len;
 162        int ret;
 163
 164        if (urgent) {
 165                /* Read status because it is toggled */
 166                status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
 167                /* Write urgent command to urgent register */
 168                MBOX_WRITEL(cmd, MBOX_URG);
 169        } else {
 170                ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
 171                if (ret)
 172                        return ret;
 173        }
 174
 175        /* write doorbell */
 176        MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
 177
 178        while (1) {
 179                ret = ~0;
 180
 181                /* Wait for doorbell from SDM */
 182                while (!MBOX_READL(MBOX_DOORBELL_FROM_SDM) && ret--)
 183                        ;
 184                if (!ret)
 185                        return -ETIMEDOUT;
 186
 187                /* clear interrupt */
 188                MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
 189
 190                if (urgent) {
 191                        u32 new_status = MBOX_READL(MBOX_STATUS);
 192
 193                        /* Urgent ACK is toggled */
 194                        if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
 195                                return 0;
 196
 197                        return -ECOMM;
 198                }
 199
 200                /* read current response offset */
 201                rout = MBOX_READL(MBOX_ROUT);
 202
 203                /* read response valid offset */
 204                rin = MBOX_READL(MBOX_RIN);
 205
 206                if (rout != rin) {
 207                        /* Response received */
 208                        resp = MBOX_READ_RESP_BUF(rout);
 209                        rout++;
 210                        /* wrapping around when it reach the buffer size */
 211                        rout %= MBOX_RESP_BUFFER_SIZE;
 212                        /* update next ROUT */
 213                        MBOX_WRITEL(rout, MBOX_ROUT);
 214
 215                        /* check client ID and ID */
 216                        if ((MBOX_RESP_CLIENT_GET(resp) ==
 217                             MBOX_CLIENT_ID_UBOOT) &&
 218                            (MBOX_RESP_ID_GET(resp) == id)) {
 219                                ret = MBOX_RESP_ERR_GET(resp);
 220                                if (ret)
 221                                        return ret;
 222
 223                                if (resp_buf_len) {
 224                                        buf_len = *resp_buf_len;
 225                                        *resp_buf_len = 0;
 226                                } else {
 227                                        buf_len = 0;
 228                                }
 229
 230                                resp_len = MBOX_RESP_LEN_GET(resp);
 231                                while (resp_len) {
 232                                        ret = mbox_polling_resp(rout);
 233                                        if (ret)
 234                                                return ret;
 235                                        /* we need to process response buffer
 236                                         * even caller doesn't need it
 237                                         */
 238                                        resp = MBOX_READ_RESP_BUF(rout);
 239                                        rout++;
 240                                        resp_len--;
 241                                        rout %= MBOX_RESP_BUFFER_SIZE;
 242                                        MBOX_WRITEL(rout, MBOX_ROUT);
 243                                        if (buf_len) {
 244                                                /* copy response to buffer */
 245                                                resp_buf[*resp_buf_len] = resp;
 246                                                (*resp_buf_len)++;
 247                                                buf_len--;
 248                                        }
 249                                }
 250                                return ret;
 251                        }
 252                }
 253        };
 254
 255        return -EIO;
 256}
 257
 258int mbox_init(void)
 259{
 260        int ret;
 261
 262        /* enable mailbox interrupts */
 263        MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
 264
 265        /* Ensure urgent request is cleared */
 266        MBOX_WRITEL(0, MBOX_URG);
 267
 268        /* Ensure the Doorbell Interrupt is cleared */
 269        MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
 270
 271        ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
 272                            NULL, 1, 0, NULL);
 273        if (ret)
 274                return ret;
 275
 276        /* Renable mailbox interrupts after MBOX_RESTART */
 277        MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
 278
 279        return 0;
 280}
 281
 282#ifdef CONFIG_CADENCE_QSPI
 283int mbox_qspi_close(void)
 284{
 285        return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
 286                             0, NULL, 0, 0, NULL);
 287}
 288
 289int mbox_qspi_open(void)
 290{
 291        int ret;
 292        u32 resp_buf[1];
 293        u32 resp_buf_len;
 294
 295        ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
 296                            0, NULL, 0, 0, NULL);
 297        if (ret) {
 298                /* retry again by closing and reopen the QSPI again */
 299                ret = mbox_qspi_close();
 300                if (ret)
 301                        return ret;
 302
 303                ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
 304                                    MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
 305                if (ret)
 306                        return ret;
 307        }
 308
 309        /* HPS will directly control the QSPI controller, no longer mailbox */
 310        resp_buf_len = 1;
 311        ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
 312                            0, NULL, 0, (u32 *)&resp_buf_len,
 313                            (u32 *)&resp_buf);
 314        if (ret)
 315                goto error;
 316
 317        /* We are getting QSPI ref clock and set into sysmgr boot register */
 318        printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]);
 319        writel(resp_buf[0],
 320               socfpga_get_sysmgr_addr() + SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
 321
 322        return 0;
 323
 324error:
 325        mbox_qspi_close();
 326
 327        return ret;
 328}
 329#endif /* CONFIG_CADENCE_QSPI */
 330
 331int mbox_reset_cold(void)
 332{
 333        int ret;
 334
 335        ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
 336                            0, NULL, 0, 0, NULL);
 337        if (ret) {
 338                /* mailbox sent failure, wait for watchdog to kick in */
 339                hang();
 340        }
 341        return 0;
 342}
 343
 344/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
 345static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
 346{
 347        u32 reconfig_status_resp_len;
 348        u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
 349        int ret;
 350
 351        reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
 352        ret = mbox_send_cmd_common(MBOX_ID_UBOOT, cmd,
 353                                   MBOX_CMD_DIRECT, 0, NULL, 0,
 354                                   &reconfig_status_resp_len,
 355                                   reconfig_status_resp);
 356
 357        if (ret)
 358                return ret;
 359
 360        /* Check for any error */
 361        ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
 362        if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
 363                return ret;
 364
 365        /* Make sure nStatus is not 0 */
 366        ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
 367        if (!(ret & RCF_PIN_STATUS_NSTATUS))
 368                return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
 369
 370        ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
 371        if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
 372                return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
 373
 374        if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
 375            (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
 376            !reconfig_status_resp[RECONFIG_STATUS_STATE])
 377                return 0;       /* configuration success */
 378
 379        return MBOX_CFGSTAT_STATE_CONFIG;
 380}
 381
 382int mbox_get_fpga_config_status(u32 cmd)
 383{
 384        return mbox_get_fpga_config_status_common(cmd);
 385}
 386
 387int __secure mbox_get_fpga_config_status_psci(u32 cmd)
 388{
 389        return mbox_get_fpga_config_status_common(cmd);
 390}
 391
 392int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
 393                  u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
 394{
 395        return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
 396                               resp_buf_len, resp_buf);
 397}
 398
 399int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
 400                                u32 *arg, u8 urgent, u32 *resp_buf_len,
 401                                u32 *resp_buf)
 402{
 403        return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
 404                               resp_buf_len, resp_buf);
 405}
 406
 407int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
 408{
 409        return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
 410}
 411
 412int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
 413                                     u32 *arg)
 414{
 415        return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
 416}
 417
 418int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
 419{
 420        return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
 421}
 422
 423int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
 424{
 425        return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
 426}
 427