From: Felipe Balbi <felipe.ba...@linux.intel.com> Intel MID platforms have few microcontrollers inside SoC, one of them is so called System Controller Unit (SCU).
Here is the driver to communicate with microcontroller. Signed-off-by: Vincent Tinelli <vincent.tine...@intel.com> Signed-off-by: Felipe Balbi <felipe.ba...@linux.intel.com> Signed-off-by: Andy Shevchenko <andriy.shevche...@linux.intel.com> --- arch/x86/Kconfig | 1 + drivers/misc/Kconfig | 6 ++ drivers/misc/Makefile | 1 + drivers/misc/intel_scu_ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++++ include/intel_scu_ipc.h | 57 +++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 drivers/misc/intel_scu_ipc.c create mode 100644 include/intel_scu_ipc.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index dfdd7564ea..6a747c332e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -83,6 +83,7 @@ endchoice # subarchitectures-specific options below config INTEL_MID bool "Intel MID platform support" + select INTEL_SCU help Select to build a U-Boot capable of supporting Intel MID (Mobile Internet Device) platform systems which do not have diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1aae4bcd07..8d81f9c51c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -167,4 +167,10 @@ config I2C_EEPROM depends on MISC help Enable a generic driver for EEPROMs attached via I2C. + +config INTEL_SCU + bool "Enable support for SCU on Intel MID platforms" + depends on INTEL_MID + default y + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e3151ea097..47551e44d6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o obj-$(CONFIG_QFW) += qfw.o +obj-$(CONFIG_INTEL_SCU) += intel_scu_ipc.o diff --git a/drivers/misc/intel_scu_ipc.c b/drivers/misc/intel_scu_ipc.c new file mode 100644 index 0000000000..19a99b0b68 --- /dev/null +++ b/drivers/misc/intel_scu_ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device.h> +#include <intel_scu_ipc.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/sizes.h> + +#define IPC_STATUS_ADDR 0x04 +#define IPC_SPTR_ADDR 0x08 +#define IPC_DPTR_ADDR 0x0C +#define IPC_READ_BUFFER 0x90 +#define IPC_WRITE_BUFFER 0x80 +#define IPC_IOC BIT(8) + +struct intel_scu { + void __iomem *base; +}; + +/* Only one for now */ +static struct intel_scu *the_scu; + +static void scu_writel(void __iomem *base, unsigned int offset, unsigned int val) +{ + writel(val, base + offset); +} + +static unsigned int scu_readl(void __iomem *base, unsigned int offset) +{ + return readl(base + offset); +} + +/* + * Command Register (Write Only): + * A write to this register results in an interrupt to the SCU core processor + * Format: + * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| + */ +static void intel_scu_ipc_send_command(struct intel_scu *scu, u32 cmd) +{ + scu_writel(scu->base, 0x00, cmd | IPC_IOC); +} + +/* + * IPC Write Buffer (Write Only): + * 16-byte buffer for sending data associated with IPC command to + * SCU. Size of the data is specified in the IPC_COMMAND_REG register + */ +static void ipc_data_writel(struct intel_scu *scu, u32 data, u32 offset) +{ + scu_writel(scu->base, IPC_WRITE_BUFFER + offset, data); +} + +/* + * Status Register (Read Only): + * Driver will read this register to get the ready/busy status of the IPC + * block and error status of the IPC command that was just processed by SCU + * Format: + * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| + */ +static u32 ipc_read_status(struct intel_scu *scu) +{ + return scu_readl(scu->base, IPC_STATUS_ADDR); +} + +static u32 ipc_data_readl(struct intel_scu *scu, u32 offset) +{ + return scu_readl(scu->base, IPC_READ_BUFFER + offset); +} + +static int intel_scu_ipc_check_status(struct intel_scu *scu) +{ + int loop_count = 3000000; + int status; + + do { + status = ipc_read_status(scu); + udelay(1); + } while ((status & BIT(0)) && --loop_count); + if (!loop_count) + return -ETIMEDOUT; + + if (status & BIT(1)) { + printf("%s() status=0x%08x\n", __func__, status); + return -EIO; + } + + return 0; +} + +static int intel_scu_ipc_raw_cmd(struct intel_scu *scu, u32 cmd, u32 sub, + u8 *in, u8 inlen, u32 *out, u32 outlen, + u32 dptr, u32 sptr) +{ + int i, err; + u32 wbuf[4]; + + if (inlen > 16) + return -EINVAL; + + memcpy(wbuf, in, inlen); + + scu_writel(scu->base, IPC_DPTR_ADDR, dptr); + scu_writel(scu->base, IPC_SPTR_ADDR, sptr); + + /** + * SRAM controller don't support 8bit write, it only supports + * 32bit write, so we have to write into the WBUF in 32bit, + * and SCU FW will use the inlen to determine the actual input + * data length in the WBUF. + */ + for (i = 0; i < DIV_ROUND_UP(inlen, 4); i++) + ipc_data_writel(scu, wbuf[i], 4 * i); + + /** + * Watchdog IPC command is an exception here using double word + * as the unit of input data size because of some historical + * reasons and SCU FW is doing so. + */ + if ((cmd & 0xff) == IPCMSG_WATCHDOG_TIMER) + inlen = DIV_ROUND_UP(inlen, 4); + + intel_scu_ipc_send_command(scu, (inlen << 16) | (sub << 12) | cmd); + err = intel_scu_ipc_check_status(scu); + + for (i = 0; i < outlen; i++) + *out++ = ipc_data_readl(scu, 4 * i); + + return err; +} + +/** + * intel_scu_ipc_simple_command - send a simple command + * @cmd: command + * @sub: sub type + * + * Issue a simple command to the SCU. Do not use this interface if + * you must then access data as any data values may be overwritten + * by another SCU access by the time this function returns. + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +int intel_scu_ipc_simple_command(int cmd, int sub) +{ + intel_scu_ipc_send_command(the_scu, sub << 12 | cmd); + + return intel_scu_ipc_check_status(the_scu); +} + +int intel_scu_ipc_command(u32 cmd, u32 sub, u8 *in, u8 inlen, u32 *out, u32 outlen) +{ + return intel_scu_ipc_raw_cmd(the_scu, cmd, sub, in, inlen, out, outlen, 0, 0); +} + +static int intel_scu_bind(struct udevice *dev) +{ + /* This device is needed straight away */ + return device_probe(dev); +} + +static int intel_scu_probe(struct udevice *dev) +{ + struct intel_scu *scu = dev_get_uclass_priv(dev); + fdt_addr_t base; + + base = dev_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + scu->base = devm_ioremap(dev, base, SZ_1K); + if (!scu->base) + return -ENOMEM; + + the_scu = scu; + + return 0; +} + +static const struct udevice_id intel_scu_match[] = { + { .compatible = "intel,scu-ipc" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(intel_scu_ipc) = { + .name = "intel_scu_ipc", + .id = UCLASS_MISC, + .of_match = intel_scu_match, + .bind = intel_scu_bind, + .probe = intel_scu_probe, + .priv_auto_alloc_size = sizeof(struct intel_scu), +}; diff --git a/include/intel_scu_ipc.h b/include/intel_scu_ipc.h new file mode 100644 index 0000000000..ded6352e10 --- /dev/null +++ b/include/intel_scu_ipc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _INTEL_SCU_IPC_H_ +#define _INTEL_SCU_IPC_H_ + +/* IPC defines the following message types */ +#define IPCMSG_WARM_RESET 0xF0 +#define IPCMSG_COLD_RESET 0xF1 +#define IPCMSG_SOFT_RESET 0xF2 +#define IPCMSG_COLD_BOOT 0xF3 +#define IPCMSG_GET_FW_REVISION 0xF4 +#define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ + +#define IPC_ERR_NONE 0 +#define IPC_ERR_CMD_NOT_SUPPORTED 1 +#define IPC_ERR_CMD_NOT_SERVICED 2 +#define IPC_ERR_UNABLE_TO_SERVICE 3 +#define IPC_ERR_CMD_INVALID 4 +#define IPC_ERR_CMD_FAILED 5 +#define IPC_ERR_EMSECURITY 6 + +/* Command id associated with message IPCMSG_VRTC */ +#define IPC_CMD_VRTC_SETTIME 1 /* Set time */ +#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */ +#define IPC_CMD_VRTC_SYNC_RTC 3 /* Sync MSIC/PMIC RTC to VRTC */ + +union ipc_ifwi_version { + u8 raw[16]; + struct { + u16 ifwi_minor; + u8 ifwi_major; + u8 hardware_id; + u32 reserved[3]; + } fw __attribute__((packed)); +} __attribute__((packed)); + +/* Issue commands to the SCU with or without data */ +int intel_scu_ipc_simple_command(int cmd, int sub); +int intel_scu_ipc_command(u32 cmd, u32 sub, u8 *in, u8 inlen, u32 *out, u32 outlen); + +enum intel_mid_cpu_type { + INTEL_CPU_CHIP_NOTMID = 0, + INTEL_MID_CPU_CHIP_LINCROFT, + INTEL_MID_CPU_CHIP_PENWELL, + INTEL_MID_CPU_CHIP_CLOVERVIEW, + INTEL_MID_CPU_CHIP_TANGIER, +}; + +static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void) +{ + return INTEL_MID_CPU_CHIP_TANGIER; +} + +#endif /* _INTEL_SCU_IPC_H_ */ -- 2.11.0 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/listinfo/u-boot