On 29-01-21, 17:32, Srinivas Kandagatla wrote: > Add support to new interrupts and update irq routine in a way > to deal with multiple pending interrupts with in a single interrupt! > > Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxx> > --- > drivers/soundwire/qcom.c | 191 ++++++++++++++++++++++++++++++--------- > 1 file changed, 146 insertions(+), 45 deletions(-) > > diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c > index d61b204dc284..c699bd51d29a 100644 > --- a/drivers/soundwire/qcom.c > +++ b/drivers/soundwire/qcom.c > @@ -28,10 +28,21 @@ > #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) > #define SWRM_INTERRUPT_STATUS 0x200 > #define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) > +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0) > #define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1) > #define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS BIT(2) > +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET BIT(3) > +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW BIT(4) > +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW BIT(5) > +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW BIT(6) > #define SWRM_INTERRUPT_STATUS_CMD_ERROR BIT(7) > +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION BIT(8) > +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH BIT(9) > #define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10) > +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 BIT(13) > +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 BIT(14) > +#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP BIT(16) > +#define SWRM_INTERRUPT_MAX 17 > #define SWRM_INTERRUPT_MASK_ADDR 0x204 > #define SWRM_INTERRUPT_CLEAR 0x208 > #define SWRM_INTERRUPT_CPU_EN 0x210 > @@ -105,11 +116,8 @@ struct qcom_swrm_ctrl { > struct device *dev; > struct regmap *regmap; > void __iomem *mmio; > - struct completion *comp; > struct completion broadcast; > struct work_struct slave_work; > - /* read/write lock */ > - spinlock_t comp_lock; > /* Port alloc/free lock */ > struct mutex port_lock; > struct mutex io_lock; > @@ -126,6 +134,7 @@ struct qcom_swrm_ctrl { > int rows_index; > unsigned long dout_port_mask; > unsigned long din_port_mask; > + u32 intr_mask; > u8 rcmd_id; > u8 wcmd_id; > struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS]; > @@ -315,6 +324,27 @@ static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *swrm, > return ret; > } > > +static int qcom_swrm_get_alert_slave(struct qcom_swrm_ctrl *ctrl) > +{ > + u32 val; > + int i; > + > + ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val); > + > + for (i = 0; i < SDW_MAX_DEVICES; i++) { > + u32 s; define at top of the function pls, also maybe better name status? > + > + s = (val >> (i * 2)); why * 2 ? Maybe add a comment for this logic > + > + if ((s & SWRM_MCP_SLV_STATUS_MASK) == SDW_SLAVE_ALERT) { > + ctrl->status[i] = s; > + return i; > + } > + } > + > + return -EINVAL; > +} > + > static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl) > { > u32 val; > @@ -333,40 +363,122 @@ static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl) > > static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) > { > - struct qcom_swrm_ctrl *ctrl = dev_id; > - u32 sts, value; > - unsigned long flags; > - > - ctrl->reg_read(ctrl, SWRM_INTERRUPT_STATUS, &sts); > - > - if (sts & SWRM_INTERRUPT_STATUS_CMD_ERROR) { > - ctrl->reg_read(ctrl, SWRM_CMD_FIFO_STATUS, &value); > - dev_err_ratelimited(ctrl->dev, > - "CMD error, fifo status 0x%x\n", > - value); > - ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1); > - } > + struct qcom_swrm_ctrl *swrm = dev_id; > + u32 value, intr_sts, intr_sts_masked; > + u32 i; > + u8 devnum = 0; > + int ret = IRQ_HANDLED; > + > + > + swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); > + intr_sts_masked = intr_sts & swrm->intr_mask; > + > +handle_irq: maybe move this into a fn and avoid a goto for non err path? > + for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { > + value = intr_sts_masked & (1 << i); > + if (!value) > + continue; > + > + switch (value) { > + case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: > + devnum = qcom_swrm_get_alert_slave(swrm); > + if (devnum < 0) { > + dev_err_ratelimited(swrm->dev, > + "no slave alert found.spurious interrupt\n"); > + } else { > + sdw_handle_slave_status(&swrm->bus, swrm->status); > + } > > - if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) || > - sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) > - schedule_work(&ctrl->slave_work); > - > - /** > - * clear the interrupt before complete() is called, as complete can > - * schedule new read/writes which require interrupts, clearing the > - * interrupt would avoid missing interrupts in such cases. > - */ > - ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, sts); > - > - if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED) { > - spin_lock_irqsave(&ctrl->comp_lock, flags); > - if (ctrl->comp) > - complete(ctrl->comp); > - spin_unlock_irqrestore(&ctrl->comp_lock, flags); > + break; > + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: > + case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: > + dev_err_ratelimited(swrm->dev, "%s: SWR new slave attached\n", > + __func__); > + qcom_swrm_get_device_status(swrm); > + sdw_handle_slave_status(&swrm->bus, swrm->status); > + break; > + case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: > + dev_err_ratelimited(swrm->dev, > + "%s: SWR bus clsh detected\n", > + __func__); > + swrm->intr_mask &= > + ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; > + swrm->reg_write(swrm, > + SWRM_INTERRUPT_CPU_EN, > + swrm->intr_mask); > + break; > + case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: > + swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); > + dev_err_ratelimited(swrm->dev, > + "%s: SWR read FIFO overflow fifo status 0x%x\n", > + __func__, value); > + break; > + case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: > + swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); > + dev_err_ratelimited(swrm->dev, > + "%s: SWR read FIFO underflow fifo status 0x%x\n", > + __func__, value); > + break; > + case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: > + swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); > + dev_err(swrm->dev, > + "%s: SWR write FIFO overflow fifo status %x\n", > + __func__, value); > + swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, 0x1); > + break; > + case SWRM_INTERRUPT_STATUS_CMD_ERROR: > + swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); > + dev_err_ratelimited(swrm->dev, > + "%s: SWR CMD error, fifo status 0x%x, flushing fifo\n", > + __func__, value); > + swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, 0x1); > + break; > + case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: > + dev_err_ratelimited(swrm->dev, > + "%s: SWR Port collision detected\n", > + __func__); > + swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; > + swrm->reg_write(swrm, > + SWRM_INTERRUPT_CPU_EN, swrm->intr_mask); > + break; > + case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: > + dev_err_ratelimited(swrm->dev, > + "%s: SWR read enable valid mismatch\n", > + __func__); > + swrm->intr_mask &= > + ~SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH; > + swrm->reg_write(swrm, > + SWRM_INTERRUPT_CPU_EN, swrm->intr_mask); > + break; > + case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: > + complete(&swrm->broadcast); > + break; > + case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2: > + break; > + case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2: > + break; > + case SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP: > + break; > + default: > + dev_err_ratelimited(swrm->dev, > + "%s: SWR unknown interrupt value: %d\n", > + __func__, value); > + ret = IRQ_NONE; > + break; > + } > } > + swrm->reg_write(swrm, SWRM_INTERRUPT_CLEAR, intr_sts); > + swrm->reg_write(swrm, SWRM_INTERRUPT_CLEAR, 0x0); > + > + swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); > + intr_sts_masked = intr_sts & swrm->intr_mask; > + > + if (intr_sts_masked) > + goto handle_irq; > > - return IRQ_HANDLED; > + return ret; > } > + > static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) > { > u32 val; > @@ -380,6 +492,7 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) > /* Disable Auto enumeration */ > ctrl->reg_write(ctrl, SWRM_ENUMERATOR_CFG_ADDR, 0); > > + ctrl->intr_mask = SWRM_INTERRUPT_STATUS_RMSK; > /* Mask soundwire interrupts */ > ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, > SWRM_INTERRUPT_STATUS_RMSK); > @@ -615,16 +728,6 @@ static u32 qcom_swrm_freq_tbl[MAX_FREQ_NUM] = { > DEFAULT_CLK_FREQ, > }; > > -static void qcom_swrm_slave_wq(struct work_struct *work) > -{ > - struct qcom_swrm_ctrl *ctrl = > - container_of(work, struct qcom_swrm_ctrl, slave_work); > - > - qcom_swrm_get_device_status(ctrl); > - sdw_handle_slave_status(&ctrl->bus, ctrl->status); > -} > - > - > static void qcom_swrm_stream_free_ports(struct qcom_swrm_ctrl *ctrl, > struct sdw_stream_runtime *stream) > { > @@ -989,11 +1092,9 @@ static int qcom_swrm_probe(struct platform_device *pdev) > > ctrl->dev = dev; > dev_set_drvdata(&pdev->dev, ctrl); > - spin_lock_init(&ctrl->comp_lock); > mutex_init(&ctrl->port_lock); > mutex_init(&ctrl->io_lock); > init_completion(&ctrl->broadcast); > - INIT_WORK(&ctrl->slave_work, qcom_swrm_slave_wq); > > ctrl->bus.ops = &qcom_swrm_ops; > ctrl->bus.port_ops = &qcom_swrm_port_ops; > -- > 2.21.0 -- ~Vinod