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 */ 23 struct 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 33 struct 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 */ 47 static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd) 48 { 49 writel(cmd, ®s->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 */ 62 static int scu_ipc_check_status(struct ipc_regs *regs) 63 { 64 int loop_count = 100000; 65 int status; 66 67 do { 68 status = readl(®s->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 85 static 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++, ®s->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(®s->rbuf[i]); 99 } 100 101 return err; 102 } 103 104 /** 105 * scu_ipc_raw_command() - IPC command with data and pointers 106 * @cmd: IPC command code 107 * @sub: IPC command sub type 108 * @in: input data of this IPC command 109 * @inlen: input data length in dwords 110 * @out: output data of this IPC command 111 * @outlen: output data length in dwords 112 * @dptr: data writing to SPTR register 113 * @sptr: data writing to DPTR register 114 * 115 * Send an IPC command to SCU with input/output data and source/dest pointers. 116 * 117 * Return: an IPC error code or 0 on success. 118 */ 119 int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, 120 int outlen, u32 dptr, u32 sptr) 121 { 122 int inbuflen = DIV_ROUND_UP(inlen, 4); 123 struct udevice *dev; 124 struct scu *scu; 125 int ret; 126 127 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev); 128 if (ret) 129 return ret; 130 131 scu = dev_get_priv(dev); 132 133 /* Up to 16 bytes */ 134 if (inbuflen > 4) 135 return -EINVAL; 136 137 writel(dptr, &scu->regs->dptr); 138 writel(sptr, &scu->regs->sptr); 139 140 /* 141 * SRAM controller doesn't support 8-bit writes, it only 142 * supports 32-bit writes, so we have to copy input data into 143 * the temporary buffer, and SCU FW will use the inlen to 144 * determine the actual input data length in the temporary 145 * buffer. 146 */ 147 148 u32 inbuf[4] = {0}; 149 150 memcpy(inbuf, in, inlen); 151 152 return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen); 153 } 154 155 /** 156 * scu_ipc_simple_command() - send a simple command 157 * @cmd: command 158 * @sub: sub type 159 * 160 * Issue a simple command to the SCU. Do not use this interface if 161 * you must then access data as any data values may be overwritten 162 * by another SCU access by the time this function returns. 163 * 164 * This function may sleep. Locking for SCU accesses is handled for 165 * the caller. 166 */ 167 int scu_ipc_simple_command(u32 cmd, u32 sub) 168 { 169 struct scu *scu; 170 struct udevice *dev; 171 int ret; 172 173 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev); 174 if (ret) 175 return ret; 176 177 scu = dev_get_priv(dev); 178 179 scu_ipc_send_command(scu->regs, sub << 12 | cmd); 180 return scu_ipc_check_status(scu->regs); 181 } 182 183 /** 184 * scu_ipc_command - command with data 185 * @cmd: command 186 * @sub: sub type 187 * @in: input data 188 * @inlen: input length in dwords 189 * @out: output data 190 * @outlen: output length in dwords 191 * 192 * Issue a command to the SCU which involves data transfers. 193 */ 194 int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen) 195 { 196 struct scu *scu; 197 struct udevice *dev; 198 int ret; 199 200 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev); 201 if (ret) 202 return ret; 203 204 scu = dev_get_priv(dev); 205 206 return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen); 207 } 208 209 static int scu_ipc_probe(struct udevice *dev) 210 { 211 struct scu *scu = dev_get_priv(dev); 212 213 scu->regs = syscon_get_first_range(X86_SYSCON_SCU); 214 215 return 0; 216 } 217 218 static const struct udevice_id scu_ipc_match[] = { 219 { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU }, 220 { /* sentinel */ } 221 }; 222 223 U_BOOT_DRIVER(scu_ipc) = { 224 .name = "scu_ipc", 225 .id = UCLASS_SYSCON, 226 .of_match = scu_ipc_match, 227 .probe = scu_ipc_probe, 228 .priv_auto_alloc_size = sizeof(struct scu), 229 }; 230