On 21/02/23 21:27, Pierre-Louis Bossart wrote: >> +static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager) >> +{ >> + u32 val; >> + u32 timeout = 0; >> + u32 retry_count = 0; >> + >> + acp_reg_writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN); >> + do { >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_EN_STATUS); >> + if (val) >> + break; >> + usleep_range(10, 50); > that's a 5x range used for all usleep_range() in this file, is this > intentional? > usleep_range(10, 20) will be enough. Will fix it. >> + } while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT); >> + >> + if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT) >> + return -ETIMEDOUT; >> + >> + /* SoundWire manager bus reset */ >> + acp_reg_writel(AMD_SDW_BUS_RESET_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + while (!(val & AMD_SDW_BUS_RESET_DONE)) { >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + if (timeout > AMD_DELAY_LOOP_ITERATION) >> + break; > so if you break here... > >> + usleep_range(1, 5); >> + timeout++; >> + } >> + if (timeout == AMD_DELAY_LOOP_ITERATION) >> + return -ETIMEDOUT; > ... this test will never be used. the timeout management looks wrong? Yes it overlooked. Will fix it. >> + timeout = 0; >> + acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + while (val) { >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); >> + if (timeout > AMD_DELAY_LOOP_ITERATION) >> + break; > same here... >> + usleep_range(1, 5); >> + timeout++; >> + } >> + if (timeout == AMD_DELAY_LOOP_ITERATION) { > ... and here. will fix it. >> + dev_err(amd_manager->dev, "Failed to reset SoundWire manager instance%d\n", >> + amd_manager->instance); >> + return -ETIMEDOUT; >> + } >> + retry_count = 0; >> + acp_reg_writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN); >> + do { >> + val = acp_reg_readl(amd_manager->mmio + ACP_SW_EN_STATUS); >> + if (!val) >> + break; >> + usleep_range(10, 50); >> + } while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT); >> + >> + if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT) >> + return -ETIMEDOUT; > that one looks correct though. > >> + return 0; >> +} >> +static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) >> +{ >> + struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask; >> + u32 val; >> + >> + mutex_lock(amd_manager->acp_sdw_lock); >> + val = acp_reg_readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); >> + val |= reg_mask->acp_sdw_intr_mask; >> + acp_reg_writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); > here you handle a read-modify-write sequence on the INTL_CNTL register... > >> + val = acp_reg_readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); > ... but here you only read from the same register. what's the purpose of > this read? This read to print the register value. This register read is really not required. we can drop dev_dbg() statement. >> + mutex_unlock(amd_manager->acp_sdw_lock); >> + dev_dbg(amd_manager->dev, "acp_ext_intr_ctrl[0x%x]:0x%x\n", >> + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val); >> + val = acp_reg_readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_STAT(amd_manager->instance)); >> + if (val) >> + acp_reg_writel(val, amd_manager->acp_mmio + >> + ACP_EXTERNAL_INTR_STAT(amd_manager->instance)); we will also remove ACP_EXTERNAL_INTR_STAT register update code. This change has side effects. ACP_EXTERNAL_INTR_STAT register should be updated in ACP PCI driver(parent driver) IRQ handler. >> + >> + acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio + >> + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); >> + acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio + >> + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); >> + acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK); > can you explain why the writes are not protected by the mutex? ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7, ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11, ACP_SW_ERROR_INTR_MASK registers are soundwire manager instance specific registers whereas ACP_EXTERNAL_INTR_CNTL register is part of ACP common space registers will be accessed by ACP PCI driver and other DMA driver modules, which needs to be protected by mutex lock. >> +} >> + >> +static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager) >> +{ >> + struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask; >> + u32 val; >> + >> + mutex_lock(amd_manager->acp_sdw_lock); >> + val = acp_reg_readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); >> + val &= ~reg_mask->acp_sdw_intr_mask; >> + acp_reg_writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); > you don't have the read here as in the enable sequence to presumably > that wasn't needed? please refer above reply. >> + mutex_unlock(amd_manager->acp_sdw_lock); >> + >> + acp_reg_writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); >> + acp_reg_writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); >> + acp_reg_writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK); > same, not clear why the writes are not protected? please refer above reply. >> +} > ... > >> +static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lword, u32 uword) >> +{ >> + u64 resp; >> + u32 resp_lower, resp_high; >> + u32 sts; >> + u32 timeout = 0; >> + >> + sts = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + while (sts & AMD_SDW_IMM_CMD_BUSY) { >> + sts = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + if (timeout > AMD_SDW_RETRY_COUNT) { >> + dev_err(amd_manager->dev, "SDW%x previous cmd status clear failed\n", >> + amd_manager->instance); >> + return -ETIMEDOUT; >> + } >> + timeout++; > no usleep_range() here? will fix it by using usleep_range(5, 10). > Also is there any merit in using the same retry count across the board, > this is about command handling, not enabling the hardware - presumably > this should be tied to the SoundWire bus frame timing, not internal delays. > >> + } >> + >> + timeout = 0; >> + if (sts & AMD_SDW_IMM_RES_VALID) { >> + dev_err(amd_manager->dev, "SDW%x manager is in bad state\n", amd_manager->instance); >> + acp_reg_writel(0x00, amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + } >> + acp_reg_writel(uword, amd_manager->mmio + ACP_SW_IMM_CMD_UPPER_WORD); >> + acp_reg_writel(lword, amd_manager->mmio + ACP_SW_IMM_CMD_LOWER_QWORD); >> + >> + sts = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + while (!(sts & AMD_SDW_IMM_RES_VALID)) { >> + sts = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + if (timeout > AMD_SDW_RETRY_COUNT) { >> + dev_err(amd_manager->dev, "SDW%x cmd response timeout occurred\n", >> + amd_manager->instance); >> + return -ETIMEDOUT; > usleep_range? will fix it by using usleep_range(5, 10). >> + } >> + timeout++; >> + } >> + resp_high = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_RESP_UPPER_WORD); >> + resp_lower = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_RESP_LOWER_QWORD); >> + timeout = 0; >> + acp_reg_writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + while ((sts & AMD_SDW_IMM_RES_VALID)) { >> + sts = acp_reg_readl(amd_manager->mmio + ACP_SW_IMM_CMD_STS); >> + if (timeout > AMD_SDW_RETRY_COUNT) { >> + dev_err(amd_manager->dev, "SDW%x cmd status retry failed\n", >> + amd_manager->instance); >> + return -ETIMEDOUT; >> + } >> + timeout++; > usleep_range() ? will fix it by using usleep_range(5, 10). >> + } >> + resp = resp_high; >> + resp = (resp << 32) | resp_lower; >> + return resp; >> +} >> + >> +static enum sdw_command_response >> +amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg) >> +{ >> + struct sdw_msg scp_msg = {0}; >> + u64 response_buf[2] = {0}; >> + u32 uword = 0, lword = 0; >> + int nack = 0, no_ack = 0; >> + int index, timeout = 0; >> + >> + scp_msg.dev_num = msg->dev_num; >> + scp_msg.addr = SDW_SCP_ADDRPAGE1; >> + scp_msg.buf = &msg->addr_page1; >> + amd_sdw_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0); >> + response_buf[0] = amd_sdw_send_cmd_get_resp(amd_manager, lword, uword); >> + scp_msg.addr = SDW_SCP_ADDRPAGE2; >> + scp_msg.buf = &msg->addr_page2; >> + amd_sdw_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0); >> + response_buf[1] = amd_sdw_send_cmd_get_resp(amd_manager, lword, uword); >> + >> + /* check response the writes */ > revisit comment, grammar does not add up - missing to/if/after? will fix it. >> + for (index = 0; index < 2; index++) { > what is the 2 here? 2 denotes SCP commands response buffer count. >> + if (response_buf[index] == -ETIMEDOUT) { >> + dev_err(amd_manager->dev, "Program SCP cmd timeout\n"); > that's one log here, possibly 2 ... we are checking inside loop. >> + timeout = 1; >> + } else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) { >> + no_ack = 1; >> + if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) { >> + nack = 1; >> + dev_err(amd_manager->dev, "Program SCP NACK received\n"); >> + } >> + } >> + } >> + >> + if (timeout) { >> + dev_err_ratelimited(amd_manager->dev, >> + "SCP_addrpage command timeout for Slave %d\n", msg->dev_num); > ... and one more here. Is this needed/useful? Even if any one of the scp command response reports timeout , we need to return timeout error. >> + return SDW_CMD_TIMEOUT; >> + } >> + >> + if (nack) { >> + dev_err_ratelimited(amd_manager->dev, >> + "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); >> + return SDW_CMD_FAIL; >> + } >> + >> + if (no_ack) { >> + dev_dbg_ratelimited(amd_manager->dev, >> + "SCP_addrpage ignored for Slave %d\n", msg->dev_num); >> + return SDW_CMD_IGNORED; >> + } >> + return SDW_CMD_OK; >> +} >> +static int amd_sdw_manager_probe(struct platform_device *pdev) >> +{ >> + const struct acp_sdw_pdata *pdata = pdev->dev.platform_data; >> + struct resource *res; >> + struct device *dev = &pdev->dev; >> + struct sdw_master_prop *prop; >> + struct sdw_bus_params *params; >> + struct amd_sdw_manager *amd_manager; >> + int ret; >> + >> + if (!pdev->dev.platform_data) { >> + dev_err(dev, "platform_data not retrieved\n"); > can this happen? not needed. we can drop this check. >> + return -ENODEV; >> + } >> + amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL); >> + if (!amd_manager) >> + return -ENOMEM; >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) >> + return -ENOMEM; >> + amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res)); >> + if (IS_ERR(amd_manager->mmio)) { >> + dev_err(dev, "mmio not found\n"); >> + return PTR_ERR(amd_manager->mmio); >> + } >> + amd_manager->instance = pdata->instance; >> + amd_manager->mmio = amd_manager->acp_mmio + >> + (amd_manager->instance * SDW_MANAGER_REG_OFFSET); >> + amd_manager->acp_sdw_lock = pdata->acp_sdw_lock; >> + amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS); >> + amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS); >> + amd_manager->dev = dev; >> + amd_manager->bus.ops = &amd_sdw_ops; >> + amd_manager->bus.port_ops = &amd_sdw_port_ops; >> + amd_manager->bus.compute_params = &amd_sdw_compute_params; >> + amd_manager->bus.clk_stop_timeout = 200; >> + amd_manager->bus.link_id = amd_manager->instance; >> + switch (amd_manager->instance) { >> + case ACP_SDW0: >> + amd_manager->num_dout_ports = AMD_SDW0_MAX_TX_PORTS; >> + amd_manager->num_din_ports = AMD_SDW0_MAX_RX_PORTS; >> + break; >> + case ACP_SDW1: >> + amd_manager->num_dout_ports = AMD_SDW1_MAX_TX_PORTS; >> + amd_manager->num_din_ports = AMD_SDW1_MAX_RX_PORTS; >> + break; >> + default: >> + return -EINVAL; >> + } >> + amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance]; >> + params = &amd_manager->bus.params; >> + params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2; >> + params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2; >> + params->col = AMD_SDW_DEFAULT_COLUMNS; >> + params->row = AMD_SDW_DEFAULT_ROWS; >> + prop = &amd_manager->bus.prop; >> + prop->clk_freq = &amd_sdw_freq_tbl[0]; >> + prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ; >> + >> + ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode); >> + if (ret) { >> + dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret); >> + return ret; >> + } >> + dev_set_drvdata(dev, amd_manager); >> + INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work); >> + /* >> + * Instead of having lengthy probe sequence, spilt probe in two and > typo: split. > >> + * use work queue for SoundWire manager startup sequence. > The wording 'startup' is confusing in that you do not have a startup > sequence as for Intel. It's just deferred probe to avoid taking too much > time. will modify the comment. >> + */ >> + schedule_work(&amd_manager->probe_work); >> + return 0; >> +}