[U-Boot] [PATCH v1 1/2] misc: Add SCU IPC driver for Intel MID platforms
Simon Glass
sjg at chromium.org
Tue Mar 14 23:50:29 UTC 2017
Hi Andy,
On 13 March 2017 at 08:05, Andy Shevchenko
<andriy.shevchenko at linux.intel.com> wrote:
> On Sun, 2017-03-12 at 14:21 -0600, Simon Glass wrote:
>> > # 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.
>
>> > +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.
>
>> and perhaps MID also.
>
> MID is explained in INTEL_MID help, though I can decode the abbreviation
> in the explanation of SCU.
>
>> > @@ -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 )
>
> Got it.
>
>> > +#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?
>
> 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];
};
>
>> > +
>> > +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)?
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.
>
>> > +/*
>> > + * 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
>
> OK.
>
>> > +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.
>
>> > +}
>> > +
>> > +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.
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.
>
>> > + 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();
>
> 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() ?
>
>> 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.
>
>> 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,
>> > 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.
OK well we can always deal with any issue later.
>
>> > +/**
>> > + * 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?
>
> What exactly? Locking?
Yes.
>
> This might be a copy'n'paste from kernel and might need an adjustment.
>
>> > +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.
>
>>
>> > + .of_match = intel_scu_match,
>> > + .bind = intel_scu_bind,
>> > + .probe = intel_scu_probe,
>> > + .priv_auto_alloc_size = sizeof(struct intel_scu),
>> > +};
>
>> > +#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.
>
>> > +
>> > +/* 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));
>>
>> __packed.
>
> OK.
>
>> 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.
>
>>
>> > +
>> > +/* 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?
>
>> > +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].
Well I don't really understand what it is for, so I'll leave it to you.
Regards,
Simon
More information about the U-Boot
mailing list