[PATCH v5 05/13] peci: Add peci-aspeed controller driver

Joel Stanley joel at jms.id.au
Thu Jan 13 15:12:16 AEDT 2022


On Wed, 12 Jan 2022 at 23:06, Iwona Winiarska <iwona.winiarska at intel.com> wrote:
>
> From: Jae Hyun Yoo <jae.hyun.yoo at linux.intel.com>
>
> ASPEED AST24xx/AST25xx/AST26xx SoCs support the PECI electrical
> interface (a.k.a PECI wire) that provides a communication channel with
> Intel processors.
> This driver allows BMC to discover devices connected to it and
> communicate with them using PECI protocol.
>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo at linux.intel.com>
> Co-developed-by: Iwona Winiarska <iwona.winiarska at intel.com>
> Signed-off-by: Iwona Winiarska <iwona.winiarska at intel.com>
> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>

The driver looks good to me. I would be happy to see it merged in its
current state.

Reviewed-by: Joel Stanley <joel at jms.id.au>

I've a few questions below that can be followed up later if need be.

> +
> +static void aspeed_peci_init_regs(struct aspeed_peci *priv)
> +{
> +       u32 val;
> +
> +       /* Clear interrupts */
> +       val = readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK;

Should that be & MASK?

As you're just sanitising the registers, you could clear the status
unconditionally:

 writel(ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_STS);

> +       writel(val, priv->base + ASPEED_PECI_INT_STS);
> +
> +       /* Set timing negotiation mode and enable interrupts */
> +       val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO);

That's a complicated way to set val to zero :)

> +       val |= ASPEED_PECI_INT_MASK;
> +       writel(val, priv->base + ASPEED_PECI_INT_CTRL);
> +
> +       val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
> +       writel(val, priv->base + ASPEED_PECI_CTRL);

This will clear the rest of the ctrl register, including the divisor
settings. Was that your intention?

Reading the rest of your driver you only call _init_regs after
_controller_enable, so I guess you're fine.

> +}
> +
> +static int aspeed_peci_check_idle(struct aspeed_peci *priv)
> +{
> +       u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
> +       int ret;
> +
> +       /*
> +        * Under normal circumstances, we expect to be idle here.
> +        * In case there were any errors/timeouts that led to the situation
> +        * where the hardware is not in idle state - we need to reset and
> +        * reinitialize it to avoid potential controller hang.
> +        */
> +       if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts)) {
> +               reset_control_assert(priv->rst);
> +
> +               ret = reset_control_deassert(priv->rst);
> +               if (ret) {
> +                       dev_err(priv->dev, "cannot deassert reset control\n");
> +                       return ret;
> +               }
> +
> +               aspeed_peci_init_regs(priv);
> +
> +               ret = clk_set_rate(priv->clk, priv->clk_frequency);
> +               if (ret < 0) {
> +                       dev_err(priv->dev, "cannot set clock frequency\n");
> +                       return ret;
> +               }
> +
> +               aspeed_peci_controller_enable(priv);
> +       }
> +
> +       return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
> +                                 cmd_sts,
> +                                 !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
> +                                 ASPEED_PECI_IDLE_CHECK_INTERVAL_US,
> +                                 ASPEED_PECI_IDLE_CHECK_TIMEOUT_US);
> +}
> +
> +static int aspeed_peci_xfer(struct peci_controller *controller,
> +                           u8 addr, struct peci_request *req)
> +{
> +       struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent);
> +       unsigned long timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
> +       u32 peci_head;
> +       int ret;
> +
> +       if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
> +           req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
> +               return -EINVAL;
> +
> +       /* Check command sts and bus idle state */
> +       ret = aspeed_peci_check_idle(priv);
> +       if (ret)
> +               return ret; /* -ETIMEDOUT */
> +
> +       spin_lock_irq(&priv->lock);
> +       reinit_completion(&priv->xfer_complete);
> +
> +       peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) |
> +                   FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) |
> +                   FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len);
> +
> +       writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
> +
> +       memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, min_t(u8, req->tx.len, 16));
> +       if (req->tx.len > 16)
> +               memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req->tx.buf + 16,
> +                           req->tx.len - 16);
> +
> +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
> +       dev_dbg(priv->dev, "HEAD : %#08x\n", peci_head);
> +       print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len);
> +#endif

The ifdef is unfortunate. Could you do this?

dev_dbg(priv->dev, "HEAD : %#08x\n", peci_head);
if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG))
       print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf,
req->tx.len);

Not a biggie though, don't let this hold up merging.

> +       priv->status = 0;
> +       writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
> +       spin_unlock_irq(&priv->lock);
> +


More information about the Linux-aspeed mailing list