diff options
| -rw-r--r-- | drivers/soundwire/qcom.c | 179 | 
1 files changed, 100 insertions, 79 deletions
| diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 0f2167433d2f..faa4c84dcf61 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -38,11 +38,13 @@  #define SWRM_CMD_FIFO_WR_CMD					0x300  #define SWRM_CMD_FIFO_RD_CMD					0x304  #define SWRM_CMD_FIFO_CMD					0x308 +#define SWRM_CMD_FIFO_FLUSH					0x1  #define SWRM_CMD_FIFO_STATUS					0x30C  #define SWRM_CMD_FIFO_CFG_ADDR					0x314  #define SWRM_CONTINUE_EXEC_ON_CMD_IGNORE			BIT(31)  #define SWRM_RD_WR_CMD_RETRIES					0x7  #define SWRM_CMD_FIFO_RD_FIFO_ADDR				0x318 +#define SWRM_RD_FIFO_CMD_ID_MASK				GENMASK(11, 8)  #define SWRM_ENUMERATOR_CFG_ADDR				0x500  #define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m)		(0x101C + 0x40 * (m))  #define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK			GENMASK(2, 0) @@ -78,13 +80,16 @@  #define SWRM_SPECIAL_CMD_ID	0xF  #define MAX_FREQ_NUM		1  #define TIMEOUT_MS		(2 * HZ) -#define QCOM_SWRM_MAX_RD_LEN	0xf +#define QCOM_SWRM_MAX_RD_LEN	0x1  #define QCOM_SDW_MAX_PORTS	14  #define DEFAULT_CLK_FREQ	9600000  #define SWRM_MAX_DAIS		0xF  #define SWR_INVALID_PARAM 0xFF  #define SWR_HSTOP_MAX_VAL 0xF  #define SWR_HSTART_MIN_VAL 0x0 +#define SWR_BROADCAST_CMD_ID    0x0F +#define SWR_MAX_CMD_ID	14 +#define MAX_FIFO_RD_RETRY 3  struct qcom_swrm_port_config {  	u8 si; @@ -103,10 +108,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 clk *hclk; @@ -120,6 +123,8 @@ struct qcom_swrm_ctrl {  	int rows_index;  	unsigned long dout_port_mask;  	unsigned long din_port_mask; +	u8 rcmd_id; +	u8 wcmd_id;  	struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS];  	struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS];  	enum sdw_slave_status status[SDW_MAX_DEVICES]; @@ -198,77 +203,106 @@ static int qcom_swrm_cpu_reg_write(struct qcom_swrm_ctrl *ctrl, int reg,  	return SDW_CMD_OK;  } -static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, -				     u8 dev_addr, u16 reg_addr) +static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, +				   u8 dev_addr, u16 reg_addr)  { -	DECLARE_COMPLETION_ONSTACK(comp); -	unsigned long flags;  	u32 val; -	int ret; - -	spin_lock_irqsave(&ctrl->comp_lock, flags); -	ctrl->comp = ∁ -	spin_unlock_irqrestore(&ctrl->comp_lock, flags); -	val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, -				SWRM_SPECIAL_CMD_ID, reg_addr); -	ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val); -	if (ret) -		goto err; - -	ret = wait_for_completion_timeout(ctrl->comp, -					  msecs_to_jiffies(TIMEOUT_MS)); +	u8 id = *cmd_id; -	if (!ret) -		ret = SDW_CMD_IGNORED; -	else -		ret = SDW_CMD_OK; -err: -	spin_lock_irqsave(&ctrl->comp_lock, flags); -	ctrl->comp = NULL; -	spin_unlock_irqrestore(&ctrl->comp_lock, flags); +	if (id != SWR_BROADCAST_CMD_ID) { +		if (id < SWR_MAX_CMD_ID) +			id += 1; +		else +			id = 0; +		*cmd_id = id; +	} +	val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); -	return ret; +	return val;  } -static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl, -				     u8 dev_addr, u16 reg_addr, -				     u32 len, u8 *rval) + +static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data, +				     u8 dev_addr, u16 reg_addr)  { -	int i, ret; -	u32 val; -	DECLARE_COMPLETION_ONSTACK(comp); -	unsigned long flags; -	spin_lock_irqsave(&ctrl->comp_lock, flags); -	ctrl->comp = ∁ -	spin_unlock_irqrestore(&ctrl->comp_lock, flags); +	u32 val; +	int ret = 0; +	u8 cmd_id = 0x0; -	val = SWRM_REG_VAL_PACK(len, dev_addr, SWRM_SPECIAL_CMD_ID, reg_addr); -	ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_RD_CMD, val); -	if (ret) -		goto err; +	if (dev_addr == SDW_BROADCAST_DEV_NUM) { +		cmd_id = SWR_BROADCAST_CMD_ID; +		val = swrm_get_packed_reg_val(&cmd_id, cmd_data, +					      dev_addr, reg_addr); +	} else { +		val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, +					      dev_addr, reg_addr); +	} -	ret = wait_for_completion_timeout(ctrl->comp, -					  msecs_to_jiffies(TIMEOUT_MS)); +	/* Its assumed that write is okay as we do not get any status back */ +	swrm->reg_write(swrm, SWRM_CMD_FIFO_WR_CMD, val); + +	/* version 1.3 or less */ +	if (swrm->version <= 0x01030000) +		usleep_range(150, 155); + +	if (cmd_id == SWR_BROADCAST_CMD_ID) { +		/* +		 * sleep for 10ms for MSM soundwire variant to allow broadcast +		 * command to complete. +		 */ +		ret = wait_for_completion_timeout(&swrm->broadcast, +						  msecs_to_jiffies(TIMEOUT_MS)); +		if (!ret) +			ret = SDW_CMD_IGNORED; +		else +			ret = SDW_CMD_OK; -	if (!ret) { -		ret = SDW_CMD_IGNORED; -		goto err;  	} else {  		ret = SDW_CMD_OK;  	} +	return ret; +} -	for (i = 0; i < len; i++) { -		ctrl->reg_read(ctrl, SWRM_CMD_FIFO_RD_FIFO_ADDR, &val); -		rval[i] = val & 0xFF; -	} +static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *swrm, +				     u8 dev_addr, u16 reg_addr, +				     u32 len, u8 *rval) +{ +	u32 cmd_data, cmd_id, val, retry_attempt = 0; + +	val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + +	/* wait for FIFO RD to complete to avoid overflow */ +	usleep_range(100, 105); +	swrm->reg_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); +	/* wait for FIFO RD CMD complete to avoid overflow */ +	usleep_range(250, 255); + +	do { +		swrm->reg_read(swrm, SWRM_CMD_FIFO_RD_FIFO_ADDR, &cmd_data); +		rval[0] = cmd_data & 0xFF; +		cmd_id = FIELD_GET(SWRM_RD_FIFO_CMD_ID_MASK, cmd_data); + +		if (cmd_id != swrm->rcmd_id) { +			if (retry_attempt < (MAX_FIFO_RD_RETRY - 1)) { +				/* wait 500 us before retry on fifo read failure */ +				usleep_range(500, 505); +				swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, +						SWRM_CMD_FIFO_FLUSH); +				swrm->reg_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); +			} +			retry_attempt++; +		} else { +			return SDW_CMD_OK; +		} -err: -	spin_lock_irqsave(&ctrl->comp_lock, flags); -	ctrl->comp = NULL; -	spin_unlock_irqrestore(&ctrl->comp_lock, flags); +	} while (retry_attempt < MAX_FIFO_RD_RETRY); -	return ret; +	dev_err(swrm->dev, "failed to read fifo: reg: 0x%x, rcmd_id: 0x%x,\ +		dev_num: 0x%x, cmd_data: 0x%x\n", +		reg_addr, swrm->rcmd_id, dev_addr, cmd_data); + +	return SDW_CMD_IGNORED;  }  static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl) @@ -291,7 +325,6 @@ 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); @@ -304,8 +337,10 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)  	}  	if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) || -	    sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) -		schedule_work(&ctrl->slave_work); +	    sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) { +		qcom_swrm_get_device_status(ctrl); +		sdw_handle_slave_status(&ctrl->bus, ctrl->status); +	}  	/**  	 * clear the interrupt before complete() is called, as complete can @@ -314,15 +349,12 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)  	 */  	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); -	} +	if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED) +		complete(&ctrl->broadcast);  	return IRQ_HANDLED;  } +  static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)  {  	u32 val; @@ -562,16 +594,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)  { @@ -930,9 +952,8 @@ 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); -	INIT_WORK(&ctrl->slave_work, qcom_swrm_slave_wq); +	init_completion(&ctrl->broadcast);  	ctrl->bus.ops = &qcom_swrm_ops;  	ctrl->bus.port_ops = &qcom_swrm_port_ops; | 
