Skip to contentSkip to conversation

misc: Add SCU IPC driver for Intel MID platforms

PATCH Superseded Andy Shevchenko 3 participants 8 messages Mar 5, 2017 – Mar 15, 2017

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 misc: Add SCU IPC driver for Intel MID platforms

From: Felipe Balbi <[email protected]>

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.

---
 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
arch/x86/Kconfig +1 -0
index dfdd7564ea..6a747c332e 100644
@@ -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
drivers/misc/Kconfig +6 -0
index 1aae4bcd07..8d81f9c51c 100644
@@ -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
drivers/misc/Makefile +1 -0
index e3151ea097..47551e44d6 100644
@@ -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
drivers/misc/intel_scu_ipc.c new file +199 -0

199 lines not on this page · raw message

include/intel_scu_ipc.h new file +57 -0

57 lines not on this page · raw message

Signature · 1 line
2.11.0
#1 lore raw

PATCH 2/2 misc: Introduce minimal PMU driver for Intel MID platforms

This simple PMU driver enables access to MMC controllers during probe so tangier_sdhci can probe and be useful.

In the future it might be expanded to cover other Intel MID platforms, that's why it's called intel_mid_pmu.c.

---
 arch/x86/Kconfig             |   2 +
 drivers/misc/Kconfig         |   6 +++
 drivers/misc/Makefile        |   1 +
 drivers/misc/intel_mid_pmu.c | 124 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 133 insertions(+)
 create mode 100644 drivers/misc/intel_mid_pmu.c
arch/x86/Kconfig +2 -0
index 6a747c332e..04310653bb 100644
@@ -83,7 +83,9 @@ endchoice
 # subarchitectures-specific options below
 config INTEL_MID
 	bool "Intel MID platform support"
+	select MISC
 	select INTEL_SCU
+	select INTEL_MID_PMU
 	help
 	  Select to build a U-Boot capable of supporting Intel MID
 	  (Mobile Internet Device) platform systems which do not have
drivers/misc/Kconfig +6 -0
index 8d81f9c51c..0222a8beed 100644
@@ -168,6 +168,12 @@ config I2C_EEPROM
 	help
 	  Enable a generic driver for EEPROMs attached via I2C.
 
+config INTEL_MID_PMU
+	bool "Support for PMU on Intel MID platforms"
+	depends on MISC
+	depends on INTEL_MID
+	default y
+
 config INTEL_SCU
 	bool "Enable support for SCU on Intel MID platforms"
 	depends on INTEL_MID
drivers/misc/Makefile +1 -0
index 47551e44d6..98a58f241c 100644
@@ -51,4 +51,5 @@ 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_MID_PMU) += intel_mid_pmu.o
 obj-$(CONFIG_INTEL_SCU) += intel_scu_ipc.o
drivers/misc/intel_mid_pmu.c new file +124 -0

124 lines not on this page · raw message

Signature · 1 line
2.11.0
#2 lore raw

PATCH 2/2 misc: Introduce minimal PMU driver for Intel MID platforms

Hi Andy,

Quoted 8 lines · Andy Shevchenko

This simple PMU driver enables access to MMC controllers during probe so tangier_sdhci can probe and be useful.

In the future it might be expanded to cover other Intel MID platforms, that's why it's called intel_mid_pmu.c.

Please consider my comments on the previous driver to apply to this one also.

One more thing - you cannot call device_probe() from the bind function. That is breaking the rules.

Instead, it should be found by the first user, perhaps using syscon or perhaps with a device_get_...() function, and that will automatically probe it.

Regards,

#3 lore raw

PATCH 2/2 misc: Introduce minimal PMU driver for Intel MID platforms

Quoted 13 lines · Simon Glass

Hi Andy,

Quoted 8 lines · Andy Shevchenko

This simple PMU driver enables access to MMC controllers during probe so tangier_sdhci can probe and be useful.

In the future it might be expanded to cover other Intel MID platforms, that's why it's called intel_mid_pmu.c.

Quoted 8 lines

Please consider my comments on the previous driver to apply to this one also.

One more thing - you cannot call device_probe() from the bind function. That is breaking the rules.

Instead, it should be found by the first user, perhaps using syscon or perhaps with a device_get_...() function, and that will automatically probe it.

While SCU works fine with this model, PMU doesn't.
MMC (sdhci) requires to power on controllers, it looks like device can't be probed at that time (too early). What do you suggest to do?

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#4 lore raw

PATCH 2/2 misc: Introduce minimal PMU driver for Intel MID platforms

Quoted 19 lines · Andy Shevchenko
Quoted 18 lines · Simon Glass

Hi Andy,

Quoted 8 lines · Andy Shevchenko

This simple PMU driver enables access to MMC controllers during probe so tangier_sdhci can probe and be useful.

In the future it might be expanded to cover other Intel MID platforms, that's why it's called intel_mid_pmu.c.

Please consider my comments on the previous driver to apply to this one also.

One more thing - you cannot call device_probe() from the bind function. That is breaking the rules.

Quoted 7 lines
Quoted 3 lines

Instead, it should be found by the first user, perhaps using syscon or perhaps with a device_get_...() function, and that will automatically probe it.

While SCU works fine with this model, PMU doesn't.
MMC (sdhci) requires to power on controllers, it looks like device can't be probed at that time (too early). What do you suggest to do?

Whoof, found three mistakes - two typos in my code and an issue in sdhci generic driver.

Works now.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#5 lore raw

PATCH 1/2 misc: Add SCU IPC driver for Intel MID platforms

In reply to Andy Shevchenko

.Hi Andy,

Quoted 37 lines · Andy Shevchenko

From: Felipe Balbi <[email protected]>

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.

---
 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
arch/x86/Kconfig +1 -0
index dfdd7564ea..6a747c332e 100644
@@ -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
drivers/misc/Kconfig +0 -0
index 1aae4bcd07..8d81f9c51c 100644

drivers/misc/Kconfig config I2C_EEPROM · line 174

Quoted 8 lines
drivers/misc/Kconfig +5 -0
@@ -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

Please add help explaining what SCU is and stands for, and perhaps MID also.

Quoted 17 lines
drivers/misc/Kconfig +1 -0
+
 endmenu
drivers/misc/Makefile +1 -0
index e3151ea097..47551e44d6 100644
@@ -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
drivers/misc/intel_scu_ipc.c new file +0 -0
new file mode 100644
index 0000000000..19a99b0b68

drivers/misc/intel_scu_ipc.c line 10

Quoted 10 lines
drivers/misc/intel_scu_ipc.c new file +10 -0
@@ -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>

This should go after dm.h. (see http://www.denx.de/wiki/U-Boot/CodingStyle )

drivers/misc/intel_scu_ipc.c line 21

Quoted 11 lines
drivers/misc/intel_scu_ipc.c new file +11 -0
+#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)

If these offsets don't change can we use a C struct to access the registers?

drivers/misc/intel_scu_ipc.c line 28

Quoted 7 lines
drivers/misc/intel_scu_ipc.c new file +7 -0
+
+struct intel_scu {
+       void __iomem *base;
+};
+
+/* Only one for now */
+static struct intel_scu *the_scu;

OK, but we cannot do this with driver model. It can be found using syscon_get_regmap_by_driver_data() perhaps?

Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +6 -0
+
+static void scu_writel(void __iomem *base, unsigned int offset, unsigned int val)
+{
+       writel(val, base + offset);
+}
+

drivers/misc/intel_scu_ipc.c line 44

Quoted 10 lines
drivers/misc/intel_scu_ipc.c new file +10 -0
+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)|

Please can you use the standard function header format, something like:

/*
* intel_scu_ipc_send_command() - send a command to the SCU *
* @scu: Pointer to SCU
* @cmd: Command to send (defined by ..?)
*/

If there is a return value, the last line would be something like:

@return torque propounder coefficient in mm

Quoted 21 lines
drivers/misc/intel_scu_ipc.c new file +21 -0
+ */
+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:

drivers/misc/intel_scu_ipc.c line 75

Quoted 10 lines
drivers/misc/intel_scu_ipc.c new file +10 -0
+ * |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);

All of these functions take scu as a parameter. I wonder if they could take scu->regs, if you use a struct for the registers.

drivers/misc/intel_scu_ipc.c line 80

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+}
+
+static int intel_scu_ipc_check_status(struct intel_scu *scu)
+{
+       int loop_count = 3000000;

3 million loops? Does that indicate some sort of problem?

drivers/misc/intel_scu_ipc.c line 88

Quoted 8 lines
drivers/misc/intel_scu_ipc.c new file +8 -0
+       int status;
+
+       do {
+               status = ipc_read_status(scu);
+               udelay(1);
+       } while ((status & BIT(0)) && --loop_count);
+       if (!loop_count)
+               return -ETIMEDOUT;

Given the long timeout, how about:

start = timer_get_us();
while (1) {
status = ...
if (!(status & BUSY))

      break;
  if ((int)(timer_get_us()  - start) > 3000000)
     return -ETIMEDOUT;
}

drivers/misc/intel_scu_ipc.c line 100

Quoted 12 lines
drivers/misc/intel_scu_ipc.c new file +12 -0
+
+       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)

Function comment. Also try to avoid u32 for integer parameters. Use uint or ulong to avoid problems with 64-bit machines.

Quoted 39 lines
drivers/misc/intel_scu_ipc.c new file +39 -0
+{
+       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;
+}
+
+/**

drivers/misc/intel_scu_ipc.c line 149

Quoted 10 lines
drivers/misc/intel_scu_ipc.c new file +10 -0
+ * 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.

It that true?

Quoted 35 lines
drivers/misc/intel_scu_ipc.c new file +35 -0
+ */
+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;

drivers/misc/intel_scu_ipc.c line 194

Quoted 10 lines
drivers/misc/intel_scu_ipc.c new file +10 -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,

Consider UCLASS_SYSCON since it allows you to find the device using an enum. See 'intel_me_syscon' for example where the device can be found using X86_SYSCON_ME

Quoted 24 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+       .of_match       = intel_scu_match,
+       .bind           = intel_scu_bind,
+       .probe          = intel_scu_probe,
+       .priv_auto_alloc_size = sizeof(struct intel_scu),
+};
include/intel_scu_ipc.h new file +13 -0
new file mode 100644
index 0000000000..ded6352e10
@@ -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

include/intel_scu_ipc.h line 23

Quoted 10 lines
include/intel_scu_ipc.h new file +10 -0
+#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

Could be an enum

Quoted 5 lines
include/intel_scu_ipc.h new file +5 -0
+
+/* 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 */

include/intel_scu_ipc.h line 38

Quoted 10 lines
include/intel_scu_ipc.h new file +10 -0
+
+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));

__packed. The inner packed does not seem to be useful. For the outer one, do you have multiple structs packed one after the other? If not, perhaps drop it?

include/intel_scu_ipc.h line 42

Quoted 4 lines
include/intel_scu_ipc.h new file +4 -0
+
+/* 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);

Full function comments. These functions should be passed a struct udevice for the device they use.

include/intel_scu_ipc.h line 55

Quoted 13 lines
include/intel_scu_ipc.h new file +13 -0
+
+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;
+}

Is this identification a function of the SCU?

Quoted 5 lines
include/intel_scu_ipc.h new file +2 -1
+
+#endif /* _INTEL_SCU_IPC_H_ */
--

2.11.0

Regards,
Simon

#6 lore raw

PATCH 1/2 misc: Add SCU IPC driver for Intel MID platforms

Quoted 8 lines · Simon Glass
Quoted 8 lines
 # 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

Thanks for review. My comments / answers below.

Quoted 7 lines
Quoted 4 lines

+config INTEL_SCU
+       bool "Enable support for SCU on Intel MID platforms" +       depends on INTEL_MID
+       default y

Please add help explaining what SCU is and stands for,

OK.

Quoted 1 line

and perhaps MID also.

MID is explained in INTEL_MID help, though I can decode the abbreviation in the explanation of SCU.

Quoted 6 lines
Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
Quoted 8 lines

drivers/misc/intel_scu_ipc.c line 10

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <intel_scu_ipc.h>

This should go after dm.h. (see http://www.denx.de/wiki/U-
Boot/CodingStyle )

Got it.

Quoted 6 lines
Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +6 -0
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+
+#define IPC_STATUS_ADDR         0x04
Quoted 8 lines

drivers/misc/intel_scu_ipc.c line 21

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+#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)

If these offsets don't change can we use a C struct to access the registers?

Hmm... How would you think it will look like?

Quoted 10 lines

drivers/misc/intel_scu_ipc.c line 28

Quoted 7 lines
drivers/misc/intel_scu_ipc.c new file +7 -0
+
+struct intel_scu {
+       void __iomem *base;
+};
+
+/* Only one for now */
+static struct intel_scu *the_scu;

OK, but we cannot do this with driver model. It can be found using syscon_get_regmap_by_driver_data() perhaps?

I'm not familiar with such technique, can you elaborate a bit (perhaps link to some documentation)?

Quoted 18 lines
Quoted 7 lines
drivers/misc/intel_scu_ipc.c new file +3 -0
+/*
+ * 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)|

Please can you use the standard function header format, something like:

/*
 * intel_scu_ipc_send_command() - send a command to the SCU *
 * @scu: Pointer to SCU
 * @cmd: Command to send (defined by ..?)
 */

Quoted 3 lines

If there is a return value, the last line would be something like:

@return torque propounder coefficient in mm

OK.

Quoted 6 lines

drivers/misc/intel_scu_ipc.c line 75

Quoted 3 lines
drivers/misc/intel_scu_ipc.c new file +3 -0
+static u32 ipc_data_readl(struct intel_scu *scu, u32 offset)
+{
+       return scu_readl(scu->base, IPC_READ_BUFFER + offset);

All of these functions take scu as a parameter. I wonder if they could take scu->regs, if you use a struct for the registers.

Maybe. I dunno.

Quoted 7 lines

drivers/misc/intel_scu_ipc.c line 80

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+}
+
+static int intel_scu_ipc_check_status(struct intel_scu *scu)
+{
+       int loop_count = 3000000;

3 million loops? Does that indicate some sort of problem?

SCU is a separate microcontroller inside SoC. It might take time for it to handle the request.

3m might be be big, but 500k is a least we would wait.

Quoted 9 lines
Quoted 8 lines

+       int status;
+
+       do {
+               status = ipc_read_status(scu); +               udelay(1);
+       } while ((status & BIT(0)) && --loop_count); +       if (!loop_count)
+               return -ETIMEDOUT;

Quoted 3 lines

Given the long timeout, how about:

start = timer_get_us();

It makes it sleepable or what? I'm afraid of the case when it would be possible to send two sequential requests before getting an ACK from SCU.

Is it possible case when we switch to time_get_us() ?

Quoted 1 line

while (1) {

Btw, I don't like at all "while (true)" style of loops.

Quoted 7 lines

   status = ...
   if (!(status & BUSY))
      break;
  if ((int)(timer_get_us()  - start) > 3000000)
     return -ETIMEDOUT;
}

Quoted 8 lines

+static int intel_scu_ipc_raw_cmd(struct intel_scu *scu, u32 cmd,

Quoted 4 lines

u32 sub,
+                                u8 *in, u8 inlen, u32 *out, u32 outlen,
+                                u32 dptr, u32 sptr)

Function comment. Also try to avoid u32 for integer parameters. Use uint or ulong to avoid problems with 64-bit machines.

They are register width / hardware related, so, I would rather not change them.

Quoted 12 lines
Quoted 11 lines

+/**
+ * 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.

Quoted 1 line

It that true?

What exactly? Locking?

This might be a copy'n'paste from kernel and might need an adjustment.

Quoted 7 lines
Quoted 3 lines

+U_BOOT_DRIVER(intel_scu_ipc) = {
+       .name           = "intel_scu_ipc",
+       .id             = UCLASS_MISC,

Consider UCLASS_SYSCON since it allows you to find the device using an enum. See 'intel_me_syscon' for example where the device can be found using X86_SYSCON_ME

OK.

Quoted 7 lines
Quoted 6 lines

+       .of_match       = intel_scu_match,
+       .bind           = intel_scu_bind,
+       .probe          = intel_scu_probe,
+       .priv_auto_alloc_size = sizeof(struct intel_scu), +};

Quoted 9 lines

include/intel_scu_ipc.h line 23

Quoted 7 lines
include/intel_scu_ipc.h new file +7 -0
+#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

Could be an enum

OK.

Quoted 10 lines
Quoted 10 lines
include/intel_scu_ipc.h new file +5 -0
+
+/* 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 */

include/intel_scu_ipc.h new file +4 -0
+
+union ipc_ifwi_version {
+       u8 raw[16];
+       struct {
Quoted 8 lines
Quoted 6 lines

+               u16 ifwi_minor;
+               u8 ifwi_major;
+               u8 hardware_id;
+               u32 reserved[3];
+       } fw __attribute__((packed));
+} __attribute__((packed));

__packed.

OK.

Quoted 3 lines

The inner packed does not seem to be useful. For the outer
one, do you have multiple structs packed one after the other? If not, perhaps drop it?

I dunno. There is very little documentation available and this code as you may notice is originally written not by me or Felipe, so, let's assume the worse case.

Quoted 9 lines
Quoted 6 lines
include/intel_scu_ipc.h new file +4 -0
+
+/* 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);

Full function comments. These functions should be passed a struct udevice for the device they use.

I have no idea how they find the right device. Can you elaborate a bit?

Quoted 6 lines

include/intel_scu_ipc.h line 55

Quoted 4 lines
include/intel_scu_ipc.h new file +4 -0
+static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void)
+{
+       return INTEL_MID_CPU_CHIP_TANGIER;
+}

Is this identification a function of the SCU?

Nope. It was previously done in a completely separate header. If you think it makes sense to leave it there, we can create a file [with, for my opinion, 20+ useless lines and few lines over the code].

Signature · 2 lines
Andy Shevchenko <[email protected]>
Intel Finland Oy
#7 lore raw

PATCH 1/2 misc: Add SCU IPC driver for Intel MID platforms

Hi Andy,

Quoted 59 lines · Andy Shevchenko
Quoted 8 lines · Simon Glass
Quoted 8 lines
 # 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

Thanks for review. My comments / answers below.

Quoted 7 lines

drivers/misc/Kconfig config I2C_EEPROM · line 174

Quoted 4 lines
drivers/misc/Kconfig +4 -0
+config INTEL_SCU
+       bool "Enable support for SCU on Intel MID platforms"
+       depends on INTEL_MID
+       default y

Please add help explaining what SCU is and stands for,

OK.

Quoted 1 line

and perhaps MID also.

MID is explained in INTEL_MID help, though I can decode the abbreviation in the explanation of SCU.

Quoted 6 lines
Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
Quoted 8 lines

drivers/misc/intel_scu_ipc.c line 10

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <intel_scu_ipc.h>

This should go after dm.h. (see http://www.denx.de/wiki/U-
Boot/CodingStyle )

Got it.

Quoted 6 lines
Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +6 -0
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+
+#define IPC_STATUS_ADDR         0x04
Quoted 8 lines

drivers/misc/intel_scu_ipc.c line 21

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+#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)

If these offsets don't change can we use a C struct to access the registers?

Quoted 1 line

Hmm... How would you think it will look like?

Perhaps something like this?

struct ipc_regs {
u32 reserved0;
u32 sptr;
u32 dptr;
u8 reserved1[0x80 - 0x10];
u32 read[4];
u32 write[4];
};

Quoted 12 lines
Quoted 11 lines

drivers/misc/intel_scu_ipc.c line 28

Quoted 8 lines
drivers/misc/intel_scu_ipc.c new file +7 -0
+
+struct intel_scu {
+       void __iomem *base;
+};
+
+/* Only one for now */
+static struct intel_scu *the_scu;

OK, but we cannot do this with driver model. It can be found using syscon_get_regmap_by_driver_data() perhaps?

Quoted 2 lines

I'm not familiar with such technique, can you elaborate a bit (perhaps link to some documentation)?

With your header file you can provide an enum:

enum {
ROCKCHIP_SYSCON_NOC,
ROCKCHIP_SYSCON_GRF,
ROCKCHIP_SYSCON_SGRF,
ROCKCHIP_SYSCON_PMU,
ROCKCHIP_SYSCON_PMUGRF,
ROCKCHIP_SYSCON_PMUSGRF,
ROCKCHIP_SYSCON_CIC,
};

In your driver:

static const struct udevice_id rk3288_syscon_ids[] = {
{ .compatible = "rockchip,rk3288-noc", .data = ROCKCHIP_SYSCON_NOC }, { .compatible = "rockchip,rk3288-grf", .data = ROCKCHIP_SYSCON_GRF }, { .compatible = "rockchip,rk3288-sgrf", .data = ROCKCHIP_SYSCON_SGRF }, { .compatible = "rockchip,rk3288-pmu", .data = ROCKCHIP_SYSCON_PMU }, { }
};

U_BOOT_DRIVER(syscon_rk3288) = {
.name = "rk3288_syscon",
.id = UCLASS_SYSCON,
.of_match = rk3288_syscon_ids,
};

The device tree has nodes with the above compatible strings, each of which becomes a syscon device.

Then in the code somewhere you can call syscon_get_by_driver_data(ROCKCHIP_SYSCON_GRF..) to get access to that particular instance of the syscon_rk3288 driver.

Quoted 42 lines
Quoted 19 lines
Quoted 8 lines
drivers/misc/intel_scu_ipc.c new file +3 -0
+/*
+ * 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)|

Please can you use the standard function header format, something like:

/*
* intel_scu_ipc_send_command() - send a command to the SCU *
* @scu: Pointer to SCU
* @cmd: Command to send (defined by ..?)
*/

Quoted 3 lines

If there is a return value, the last line would be something like:

@return torque propounder coefficient in mm

OK.

Quoted 6 lines

drivers/misc/intel_scu_ipc.c line 75

Quoted 3 lines
drivers/misc/intel_scu_ipc.c new file +3 -0
+static u32 ipc_data_readl(struct intel_scu *scu, u32 offset)
+{
+       return scu_readl(scu->base, IPC_READ_BUFFER + offset);

All of these functions take scu as a parameter. I wonder if they could take scu->regs, if you use a struct for the registers.

Maybe. I dunno.

Quoted 7 lines

drivers/misc/intel_scu_ipc.c line 80

Quoted 5 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+}
+
+static int intel_scu_ipc_check_status(struct intel_scu *scu)
+{
+       int loop_count = 3000000;

3 million loops? Does that indicate some sort of problem?

Quoted 4 lines

SCU is a separate microcontroller inside SoC. It might take time for it to handle the request.

3m might be be big, but 500k is a least we would wait.

So that's at least half a second. Hopefully most systems would be fully booted by then! :-)

Anyway I guess there is nothing you can do about this.

Quoted 14 lines
Quoted 10 lines

drivers/misc/intel_scu_ipc.c line 88

Quoted 9 lines
drivers/misc/intel_scu_ipc.c new file +8 -0
+       int status;
+
+       do {
+               status = ipc_read_status(scu);
+               udelay(1);
+       } while ((status & BIT(0)) && --loop_count);
+       if (!loop_count)
+               return -ETIMEDOUT;
Quoted 3 lines

Given the long timeout, how about:

start = timer_get_us();

Quoted 8 lines

It makes it sleepable or what? I'm afraid of the case when it would be possible to send two sequential requests before getting an ACK from SCU.

Is it possible case when we switch to time_get_us() ?

Quoted 1 line

while (1) {

Btw, I don't like at all "while (true)" style of loops.

OK, well you can perhaps adjust it to check the timeout instead the loop. But really you are running forever until a timeout or condition change.

Quoted 14 lines
Quoted 14 lines
   status = ...
   if (!(status & BUSY))
      break;
  if ((int)(timer_get_us()  - start) > 3000000)
     return -ETIMEDOUT;
}

+static int intel_scu_ipc_raw_cmd(struct intel_scu *scu, u32 cmd,

Quoted 4 lines

u32 sub,
+ u8 *in, u8 inlen, u32 *out, u32
outlen,
+ u32 dptr, u32 sptr)

Quoted 5 lines
Quoted 2 lines

Function comment. Also try to avoid u32 for integer parameters. Use uint or ulong to avoid problems with 64-bit machines.

They are register width / hardware related, so, I would rather not change them.

OK well we can always deal with any issue later.

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

+/**
+ * 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.

Quoted 3 lines
Quoted 1 line

It that true?

What exactly? Locking?

Yes.

Quoted 53 lines

This might be a copy'n'paste from kernel and might need an adjustment.

Quoted 7 lines

drivers/misc/intel_scu_ipc.c line 194

Quoted 3 lines
drivers/misc/intel_scu_ipc.c new file +3 -0
+U_BOOT_DRIVER(intel_scu_ipc) = {
+       .name           = "intel_scu_ipc",
+       .id             = UCLASS_MISC,

Consider UCLASS_SYSCON since it allows you to find the device using an enum. See 'intel_me_syscon' for example where the device can be found using X86_SYSCON_ME

OK.

Quoted 7 lines
Quoted 6 lines
drivers/misc/intel_scu_ipc.c new file +5 -0
+       .of_match       = intel_scu_match,
+       .bind           = intel_scu_bind,
+       .probe          = intel_scu_probe,
+       .priv_auto_alloc_size = sizeof(struct intel_scu),
+};
Quoted 9 lines

include/intel_scu_ipc.h line 23

Quoted 7 lines
include/intel_scu_ipc.h new file +7 -0
+#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

Could be an enum

OK.

Quoted 10 lines
Quoted 10 lines
include/intel_scu_ipc.h new file +5 -0
+
+/* 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 */

include/intel_scu_ipc.h new file +4 -0
+
+union ipc_ifwi_version {
+       u8 raw[16];
+       struct {
Quoted 8 lines

include/intel_scu_ipc.h line 38

Quoted 6 lines
include/intel_scu_ipc.h new file +6 -0
+               u16 ifwi_minor;
+               u8 ifwi_major;
+               u8 hardware_id;
+               u32 reserved[3];
+       } fw __attribute__((packed));
+} __attribute__((packed));

__packed.

OK.

Quoted 7 lines
Quoted 3 lines

The inner packed does not seem to be useful. For the outer
one, do you have multiple structs packed one after the other? If not, perhaps drop it?

I dunno. There is very little documentation available and this code as you may notice is originally written not by me or Felipe, so, let's assume the worse case.

Well how about testing it without? It probably works fine. Having said that, on Intel CPUs there is not really much of a cost for unaligned things.

Quoted 20 lines
Quoted 10 lines
Quoted 7 lines
include/intel_scu_ipc.h new file +4 -0
+
+/* 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);

Full function comments. These functions should be passed a struct udevice for the device they use.

I have no idea how they find the right device. Can you elaborate a bit?

Quoted 6 lines

include/intel_scu_ipc.h line 55

Quoted 4 lines
include/intel_scu_ipc.h new file +4 -0
+static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void)
+{
+       return INTEL_MID_CPU_CHIP_TANGIER;
+}

Is this identification a function of the SCU?

Quoted 3 lines

Nope. It was previously done in a completely separate header. If you think it makes sense to leave it there, we can create a file [with, for my opinion, 20+ useless lines and few lines over the code].

Well I don't really understand what it is for, so I'll leave it to you.

Regards,
Simon