uboot/arch/x86/lib/scu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2017 Intel Corporation
   4 *
   5 * Intel Mobile Internet Devices (MID) based on Intel Atom SoCs have few
   6 * microcontrollers inside to do some auxiliary tasks. One of such
   7 * microcontroller is System Controller Unit (SCU) which, in particular,
   8 * is servicing watchdog and controlling system reset function.
   9 *
  10 * This driver enables IPC channel to SCU.
  11 */
  12#include <common.h>
  13#include <dm.h>
  14#include <regmap.h>
  15#include <syscon.h>
  16#include <asm/cpu.h>
  17#include <asm/scu.h>
  18#include <linux/errno.h>
  19#include <linux/io.h>
  20#include <linux/kernel.h>
  21
  22/* SCU register map */
  23struct ipc_regs {
  24        u32 cmd;
  25        u32 status;
  26        u32 sptr;
  27        u32 dptr;
  28        u32 reserved[28];
  29        u32 wbuf[4];
  30        u32 rbuf[4];
  31};
  32
  33struct scu {
  34        struct ipc_regs *regs;
  35};
  36
  37/**
  38 * scu_ipc_send_command() - send command to SCU
  39 * @regs: register map of SCU
  40 * @cmd: command
  41 *
  42 * Command Register (Write Only):
  43 * A write to this register results in an interrupt to the SCU core processor
  44 * Format:
  45 * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
  46 */
  47static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
  48{
  49        writel(cmd, &regs->cmd);
  50}
  51
  52/**
  53 * scu_ipc_check_status() - check status of last command
  54 * @regs: register map of SCU
  55 *
  56 * Status Register (Read Only):
  57 * Driver will read this register to get the ready/busy status of the IPC
  58 * block and error status of the IPC command that was just processed by SCU
  59 * Format:
  60 * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
  61 */
  62static int scu_ipc_check_status(struct ipc_regs *regs)
  63{
  64        int loop_count = 100000;
  65        int status;
  66
  67        do {
  68                status = readl(&regs->status);
  69                if (!(status & BIT(0)))
  70                        break;
  71
  72                udelay(1);
  73        } while (--loop_count);
  74        if (!loop_count)
  75                return -ETIMEDOUT;
  76
  77        if (status & BIT(1)) {
  78                printf("%s() status=0x%08x\n", __func__, status);
  79                return -EIO;
  80        }
  81
  82        return 0;
  83}
  84
  85static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
  86                       u32 *in, int inlen, u32 *out, int outlen)
  87{
  88        int i, err;
  89
  90        for (i = 0; i < inlen; i++)
  91                writel(*in++, &regs->wbuf[i]);
  92
  93        scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
  94        err = scu_ipc_check_status(regs);
  95
  96        if (!err) {
  97                for (i = 0; i < outlen; i++)
  98                        *out++ = readl(&regs->rbuf[i]);
  99        }
 100
 101        return err;
 102}
 103
 104/**
 105 * scu_ipc_simple_command() - send a simple command
 106 * @cmd: command
 107 * @sub: sub type
 108 *
 109 * Issue a simple command to the SCU. Do not use this interface if
 110 * you must then access data as any data values may be overwritten
 111 * by another SCU access by the time this function returns.
 112 *
 113 * This function may sleep. Locking for SCU accesses is handled for
 114 * the caller.
 115 */
 116int scu_ipc_simple_command(u32 cmd, u32 sub)
 117{
 118        struct scu *scu;
 119        struct udevice *dev;
 120        int ret;
 121
 122        ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
 123        if (ret)
 124                return ret;
 125
 126        scu = dev_get_priv(dev);
 127
 128        scu_ipc_send_command(scu->regs, sub << 12 | cmd);
 129        return scu_ipc_check_status(scu->regs);
 130}
 131
 132int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
 133{
 134        struct scu *scu;
 135        struct udevice *dev;
 136        int ret;
 137
 138        ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
 139        if (ret)
 140                return ret;
 141
 142        scu = dev_get_priv(dev);
 143
 144        return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
 145}
 146
 147static int scu_ipc_probe(struct udevice *dev)
 148{
 149        struct scu *scu = dev_get_priv(dev);
 150
 151        scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
 152
 153        return 0;
 154}
 155
 156static const struct udevice_id scu_ipc_match[] = {
 157        { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
 158        { /* sentinel */ }
 159};
 160
 161U_BOOT_DRIVER(scu_ipc) = {
 162        .name           = "scu_ipc",
 163        .id             = UCLASS_SYSCON,
 164        .of_match       = scu_ipc_match,
 165        .probe          = scu_ipc_probe,
 166        .priv_auto_alloc_size = sizeof(struct scu),
 167};
 168