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