Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7939c3e authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "spi: qcom-geni-se: Add SSR support for SSC QUPs"

parents 4bb87a13 47004e09
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ Optional properties:
				and Corex/2x paths.
- qcom,vote-for-bw: Boolean flag to check if ab/ib vote should be given
		    as bandwidth or BCM threashold.
- qcom,subsys-name: SSC QUPv3 subsystem name for SSR notification registration.

Optional subnodes:
qcom,iommu_qupv3_geni_se_cb:	Child node representing the QUPV3 context
@@ -31,6 +32,7 @@ Subnode Required properties:
Example:
	qupv3_0: qcom,qupv3_0_geni_se@8c0000 {
		compatible = "qcom,qupv3-geni-se";
		qcom,subsys-name = "adsp";
		reg = <0x8c0000 0x6000>;
		qcom,bus-mas-id = <100>;
		qcom,bus-slv-id = <300>;
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ Optional properties:
- qcom,rt:	Specifies if the framework worker thread for this
		controller device should have "real-time" priority.
- qcom,disable-autosuspend: Specifies to disable runtime PM auto suspend.
- ssr-enable:	Required only for SSC QupV3 client for SSR notification.

SPI slave nodes must be children of the SPI master node and can contain
the following properties.
+129 −3
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@
#define MAX_CLK_PERF_LEVEL 32
static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000,
				100000000, 150000000, 200000000, 236000000};
/* SCM Call Id */
#define SSR_SCM_CMD	0x1

struct bus_vectors {
	int src;
@@ -89,6 +91,7 @@ struct bus_vectors {
 * @update:		Usecase index for icb voting.
 * @vote_for_bw:	To check if we have to vote for BW or BCM threashold
			in ab/ib ICB voting.
 * @struct ssc_qup_ssr: Structure to represent SSC Qupv3 SSR Structure.
 */
struct geni_se_device {
	struct device *dev;
@@ -125,6 +128,7 @@ struct geni_se_device {
	struct msm_bus_scale_pdata *pdata;
	int update;
	bool vote_for_bw;
	struct ssc_qup_ssr ssr;
};

#define HW_VER_MAJOR_MASK GENMASK(31, 28)
@@ -351,6 +355,101 @@ static int geni_se_select_fifo_mode(void __iomem *base)
	return 0;
}

static ssize_t ssc_qup_state_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);

	return snprintf(buf, sizeof(int), "%d\n",
				!geni_se_dev->ssr.is_ssr_down);
}

static DEVICE_ATTR_RO(ssc_qup_state);

static void geni_se_ssc_qup_down(struct geni_se_device *dev)
{
	struct se_geni_rsc *rsc = NULL;

	dev->ssr.is_ssr_down = true;
	list_for_each_entry(rsc, &dev->ssr.active_list_head,
					rsc_ssr.active_list) {
		rsc->rsc_ssr.force_suspend(rsc->ctrl_dev);
	}
}

static void geni_se_ssc_qup_up(struct geni_se_device *dev)
{
	int ret = 0;
	struct scm_desc desc;
	struct se_geni_rsc *rsc = NULL;

	/* Passing dummy argument as it is scm call requirement */
	desc.args[0] = 0x0;
	desc.arginfo = SCM_ARGS(1, SCM_VAL);

	ret = scm_call2(SCM_SIP_FNID(TZ_SVC_QUP_FW_LOAD, SSR_SCM_CMD), &desc);
	if (ret) {
		dev_err(dev->dev, "Unable to load firmware after SSR\n");
		return;
	}

	list_for_each_entry(rsc, &dev->ssr.active_list_head,
					rsc_ssr.active_list) {
		rsc->rsc_ssr.force_resume(rsc->ctrl_dev);
	}

	dev->ssr.is_ssr_down = false;
}

static int geni_se_ssr_notify_block(struct notifier_block *n,
						unsigned long code, void *_cmd)
{
	struct ssc_qup_nb *ssc_qup_nb = container_of(n, struct ssc_qup_nb, nb);
	struct ssc_qup_ssr *ssr = container_of(ssc_qup_nb, struct ssc_qup_ssr,
					ssc_qup_nb);
	struct geni_se_device *dev = container_of(ssr, struct geni_se_device,
						ssr);

	switch (code) {
	case SUBSYS_BEFORE_SHUTDOWN:
		geni_se_ssc_qup_down(dev);
		GENI_SE_DBG(dev->log_ctx, false, NULL,
				"SSR notification before power down\n");
		break;
	case SUBSYS_AFTER_POWERUP:
		if (dev->ssr.probe_completed)
			geni_se_ssc_qup_up(dev);
		else
			dev->ssr.probe_completed = true;

		GENI_SE_DBG(dev->log_ctx, false, NULL,
				"SSR notification after power up\n");
		break;
	default:
		break;
	}

	return 0;
}

static int geni_se_ssc_qup_ssr_reg(struct geni_se_device *dev)
{
	dev->ssr.ssc_qup_nb.nb.notifier_call = geni_se_ssr_notify_block;
	dev->ssr.ssc_qup_nb.next = subsys_notif_register_notifier(
				dev->ssr.subsys_name, &dev->ssr.ssc_qup_nb.nb);

	if (IS_ERR_OR_NULL(dev->ssr.ssc_qup_nb.next)) {
		dev_err(dev->dev,
			"subsys_notif_register_notifier failed %ld\n",
			PTR_ERR(dev->ssr.ssc_qup_nb.next));
		return PTR_ERR(dev->ssr.ssc_qup_nb.next);
	}

	GENI_SE_DBG(dev->log_ctx, false, NULL, "SSR registration done\n");

	return 0;
}

static int geni_se_select_dma_mode(void __iomem *base)
{
	int proto = get_se_proto(base);
@@ -1096,6 +1195,12 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,

	INIT_LIST_HEAD(&rsc->ab_list);
	INIT_LIST_HEAD(&rsc->ib_list);
	if (geni_se_dev->ssr.subsys_name && rsc->rsc_ssr.ssr_enable) {
		INIT_LIST_HEAD(&rsc->rsc_ssr.active_list);
		list_add(&rsc->rsc_ssr.active_list,
				&geni_se_dev->ssr.active_list_head);
	}

	ret = geni_se_iommu_map_and_attach(geni_se_dev);
	if (ret)
		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
@@ -1836,13 +1941,27 @@ static int geni_se_probe(struct platform_device *pdev)
	ret = of_platform_populate(dev->of_node, geni_se_dt_match, NULL, dev);
	if (ret) {
		dev_err(dev, "%s: Error populating children\n", __func__);
		devm_iounmap(dev, geni_se_dev->base);
		devm_kfree(dev, geni_se_dev);
		return ret;
	}

	ret = of_property_read_string(geni_se_dev->dev->of_node,
			"qcom,subsys-name", &geni_se_dev->ssr.subsys_name);
	if (!ret) {
		INIT_LIST_HEAD(&geni_se_dev->ssr.active_list_head);
		geni_se_dev->ssr.probe_completed = false;
		ret = geni_se_ssc_qup_ssr_reg(geni_se_dev);
		if (ret) {
			dev_err(dev, "Unable to register SSR notification\n");
			return ret;
		}

		sysfs_create_file(&geni_se_dev->dev->kobj,
			 &dev_attr_ssc_qup_state.attr);
	}

	GENI_SE_DBG(geni_se_dev->log_ctx, false, NULL,
		    "%s: Probe successful\n", __func__);
	return ret;
	return 0;
}

static int geni_se_remove(struct platform_device *pdev)
@@ -1854,6 +1973,13 @@ static int geni_se_remove(struct platform_device *pdev)
		arm_iommu_detach_device(geni_se_dev->cb_dev);
		arm_iommu_release_mapping(geni_se_dev->iommu_map);
	}
	if (geni_se_dev->ssr.subsys_name) {
		subsys_notif_unregister_notifier(
				geni_se_dev->ssr.ssc_qup_nb.next,
				&geni_se_dev->ssr.ssc_qup_nb.nb);
		sysfs_remove_file(&geni_se_dev->dev->kobj,
				&dev_attr_ssc_qup_state.attr);
	}
	ipc_log_context_destroy(geni_se_dev->log_ctx);
	devm_iounmap(dev, geni_se_dev->base);
	devm_kfree(dev, geni_se_dev);
+133 −4
Original line number Diff line number Diff line
@@ -124,6 +124,12 @@ struct spi_geni_gsi {
	struct gsi_desc_cb desc_cb;
};

struct spi_geni_ssr {
	struct mutex ssr_lock;
	bool is_ssr_down;
	bool xfer_prepared;
};

struct spi_geni_master {
	struct se_geni_rsc spi_rsc;
	resource_size_t phys_addr;
@@ -163,9 +169,12 @@ struct spi_geni_master {
	bool shared_se;
	bool dis_autosuspend;
	bool cmd_done;
	struct spi_geni_ssr spi_ssr;
};

static void spi_slv_setup(struct spi_geni_master *mas);
static int ssr_spi_force_suspend(struct device *dev);
static int ssr_spi_force_resume(struct device *dev);

static ssize_t show_slave_state(struct device *dev,
		struct device_attribute *attr, char *buf)
@@ -788,6 +797,13 @@ static int spi_geni_prepare_message(struct spi_master *spi,
	int ret = 0;
	struct spi_geni_master *mas = spi_master_get_devdata(spi);

	mutex_lock(&mas->spi_ssr.ssr_lock);
	/* Bail out if prepare_transfer didn't happen due to SSR */
	if (mas->spi_ssr.is_ssr_down || !mas->spi_ssr.xfer_prepared) {
		mutex_unlock(&mas->spi_ssr.ssr_lock);
		return -EINVAL;
	}

	mas->cur_xfer_mode = select_xfer_mode(spi, spi_msg);

	if (mas->cur_xfer_mode < 0) {
@@ -803,6 +819,7 @@ static int spi_geni_prepare_message(struct spi_master *spi,
		geni_se_select_mode(mas->base, mas->cur_xfer_mode);
		ret = setup_fifo_params(spi_msg->spi, spi);
	}
	mutex_unlock(&mas->spi_ssr.ssr_lock);

	return ret;
}
@@ -826,6 +843,19 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi)
	u32 max_speed = spi->cur_msg->spi->max_speed_hz;
	struct se_geni_rsc *rsc = &mas->spi_rsc;

	mutex_lock(&mas->spi_ssr.ssr_lock);
	if (mas->spi_ssr.is_ssr_down) {
		/*
		 * xfer_prepared will be set to true once prepare_transfer
		 * hardware is complete.
		 * It used in prepare_message and transfer_one to bail out
		 * during SSR.
		 */
		mas->spi_ssr.xfer_prepared = false;
		mutex_unlock(&mas->spi_ssr.ssr_lock);
		return 0;
	}

	/* Adjust the IB based on the max speed of the slave.*/
	rsc->ib = max_speed * DEFAULT_BUS_WIDTH;
	if (mas->shared_se) {
@@ -864,6 +894,7 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi)

		if (unlikely(proto != SPI_SLAVE)) {
			dev_err(mas->dev, "Invalid proto %d\n", proto);
			mutex_unlock(&mas->spi_ssr.ssr_lock);
			return -ENXIO;
		}
	}
@@ -877,6 +908,7 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi)
		proto = get_se_proto(mas->base);
		if ((unlikely(proto != SPI)) && (!spi->slave)) {
			dev_err(mas->dev, "Invalid proto %d\n", proto);
			mutex_unlock(&mas->spi_ssr.ssr_lock);
			return -ENXIO;
		}

@@ -968,6 +1000,8 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi)
					"Auto Suspend is disabled\n");
	}
exit_prepare_transfer_hardware:
	mas->spi_ssr.xfer_prepared = true;
	mutex_unlock(&mas->spi_ssr.ssr_lock);
	return ret;
}

@@ -975,6 +1009,16 @@ static int spi_geni_unprepare_transfer_hardware(struct spi_master *spi)
{
	struct spi_geni_master *mas = spi_master_get_devdata(spi);
	int count = 0;

	mutex_lock(&mas->spi_ssr.ssr_lock);
	if (mas->spi_ssr.is_ssr_down || !mas->spi_ssr.xfer_prepared) {
		/* Call runtime_put to match get in prepare_transfer */
		pm_runtime_put_noidle(mas->dev);
		mutex_unlock(&mas->spi_ssr.ssr_lock);
		return -EINVAL;
	}
	mutex_unlock(&mas->spi_ssr.ssr_lock);

	if (mas->shared_se) {
		struct se_geni_rsc *rsc;
		int ret = 0;
@@ -1183,13 +1227,24 @@ static int spi_geni_transfer_one(struct spi_master *spi,
		return -EINVAL;
	}

	mutex_lock(&mas->spi_ssr.ssr_lock);
	if (mas->spi_ssr.is_ssr_down || !mas->spi_ssr.xfer_prepared) {
		mutex_unlock(&mas->spi_ssr.ssr_lock);
		return -EINVAL;
	}

	if (mas->cur_xfer_mode != GSI_DMA) {
		reinit_completion(&mas->xfer_done);
		setup_fifo_xfer(xfer, mas, slv->mode, spi);
		if (spi->slave)
			spi->slave_state = true;
		mutex_unlock(&mas->spi_ssr.ssr_lock);
		timeout = wait_for_completion_timeout(&mas->xfer_done,
					msecs_to_jiffies(SPI_XFER_TIMEOUT_MS));
		mutex_lock(&mas->spi_ssr.ssr_lock);
		if (mas->spi_ssr.is_ssr_down)
			goto err_ssr_transfer_one;

		if (spi->slave)
			spi->slave_state = false;

@@ -1256,16 +1311,20 @@ static int spi_geni_transfer_one(struct spi_master *spi,
			}
		}
	}
	mutex_unlock(&mas->spi_ssr.ssr_lock);
	return ret;
err_gsi_geni_transfer_one:
	geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
	dmaengine_terminate_all(mas->tx);
	mutex_unlock(&mas->spi_ssr.ssr_lock);
	return ret;
err_fifo_geni_transfer_one:
	if (!spi->slave)
		handle_fifo_timeout(mas, xfer);
	if (spi->slave)
		geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
err_ssr_transfer_one:
	mutex_unlock(&mas->spi_ssr.ssr_lock);
	return ret;
}

@@ -1302,6 +1361,8 @@ static void geni_spi_handle_tx(struct spi_geni_master *mas)
		int bytes_per_fifo = tx_fifo_width;
		int bytes_to_write = 0;

		if (mas->spi_ssr.is_ssr_down)
			break;
		if ((mas->tx_fifo_width % mas->cur_word_len))
			bytes_per_fifo =
				(mas->cur_word_len / BITS_PER_BYTE) + 1;
@@ -1314,7 +1375,7 @@ static void geni_spi_handle_tx(struct spi_geni_master *mas)
		mb();
	}
	mas->tx_rem_bytes -= max_bytes;
	if (!mas->tx_rem_bytes) {
	if (!mas->tx_rem_bytes && !mas->spi_ssr.is_ssr_down) {
		geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
		/* Barrier here before return to prevent further ISRs */
		mb();
@@ -1325,14 +1386,18 @@ static void geni_spi_handle_rx(struct spi_geni_master *mas)
{
	int i = 0;
	int fifo_width = (mas->tx_fifo_width >> 3);
	u32 rx_fifo_status = geni_read_reg(mas->base, SE_GENI_RX_FIFO_STATUS);
	u32 rx_fifo_status;
	int rx_bytes = 0;
	int rx_wc = 0;
	u8 *rx_buf = NULL;

	if (mas->spi_ssr.is_ssr_down)
		return;

	if (!mas->cur_xfer)
		return;

	rx_fifo_status = geni_read_reg(mas->base, SE_GENI_RX_FIFO_STATUS);
	rx_buf = mas->cur_xfer->rx_buf;
	rx_wc = (rx_fifo_status & RX_FIFO_WC_MSK);
	if (rx_fifo_status & RX_LAST) {
@@ -1358,6 +1423,8 @@ static void geni_spi_handle_rx(struct spi_geni_master *mas)
		int read_bytes = 0;
		int j;

		if (mas->spi_ssr.is_ssr_down)
			break;
		if ((mas->tx_fifo_width % mas->cur_word_len))
			bytes_per_fifo =
				(mas->cur_word_len / BITS_PER_BYTE) + 1;
@@ -1380,7 +1447,15 @@ static irqreturn_t geni_spi_irq(int irq, void *data)
				"%s: device is suspended\n", __func__);
		goto exit_geni_spi_irq;
	}

	if (mas->spi_ssr.is_ssr_down) {
		mas->cmd_done = false;
		complete(&mas->xfer_done);
		dev_err(mas->dev, "IRQ at SSR down\n");
		return IRQ_HANDLED;
	}
	m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);

	if (mas->cur_xfer_mode == FIFO_MODE) {
		if ((m_irq & M_RX_FIFO_WATERMARK_EN) ||
						(m_irq & M_RX_FIFO_LAST_EN))
@@ -1436,6 +1511,7 @@ static irqreturn_t geni_spi_irq(int irq, void *data)
			mas->cmd_done = true;
	}
exit_geni_spi_irq:
	if (!mas->spi_ssr.is_ssr_down)
		geni_write_reg(m_irq, mas->base, SE_GENI_M_IRQ_CLEAR);
	if (mas->cmd_done) {
		mas->cmd_done = false;
@@ -1488,6 +1564,8 @@ static int spi_geni_probe(struct platform_device *pdev)
	}
	geni_mas->wrapper_dev = &wrapper_pdev->dev;
	geni_mas->spi_rsc.wrapper_dev = &wrapper_pdev->dev;
	rsc->rsc_ssr.ssr_enable = of_property_read_bool(pdev->dev.of_node,
			"ssr-enable");
	ret = geni_se_resources_init(rsc, SPI_CORE2X_VOTE,
				     (DEFAULT_SE_CLK * DEFAULT_BUS_WIDTH));
	if (ret) {
@@ -1624,10 +1702,12 @@ static int spi_geni_probe(struct platform_device *pdev)
	spi->unprepare_transfer_hardware
			= spi_geni_unprepare_transfer_hardware;
	spi->auto_runtime_pm = false;

	rsc->rsc_ssr.force_suspend = ssr_spi_force_suspend;
	rsc->rsc_ssr.force_resume = ssr_spi_force_resume;
	init_completion(&geni_mas->xfer_done);
	init_completion(&geni_mas->tx_cb);
	init_completion(&geni_mas->rx_cb);
	mutex_init(&geni_mas->spi_ssr.ssr_lock);
	pm_runtime_set_suspended(&pdev->dev);
	if (!geni_mas->dis_autosuspend) {
		pm_runtime_set_autosuspend_delay(&pdev->dev,
@@ -1691,6 +1771,12 @@ static int spi_geni_runtime_resume(struct device *dev)
	struct spi_master *spi = get_spi_master(dev);
	struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);

	if (geni_mas->spi_ssr.is_ssr_down) {
		GENI_SE_ERR(geni_mas->ipc, false, NULL,
			"%s: Error runtime resume in SSR down\n", __func__);
		return -EAGAIN;
	}

	if (geni_mas->shared_se) {
		ret = se_geni_clks_on(&geni_mas->spi_rsc);
		if (ret)
@@ -1755,6 +1841,49 @@ static int spi_geni_suspend(struct device *dev)
}
#endif

static int ssr_spi_force_suspend(struct device *dev)
{
	struct spi_master *spi = get_spi_master(dev);
	struct spi_geni_master *mas = spi_master_get_devdata(spi);
	int ret = 0;

	mutex_lock(&mas->spi_ssr.ssr_lock);
	mas->spi_ssr.xfer_prepared = false;
	disable_irq(mas->irq);
	mas->spi_ssr.is_ssr_down = true;
	complete(&mas->xfer_done);

	if (!pm_runtime_status_suspended(mas->dev)) {
		ret = spi_geni_runtime_suspend(mas->dev);
		if (ret) {
			dev_err(mas->dev, "runtime suspend failed %d\n", ret);
		} else {
			pm_runtime_disable(mas->dev);
			pm_runtime_set_suspended(mas->dev);
			pm_runtime_enable(mas->dev);
		}
	}

	GENI_SE_DBG(mas->ipc, false, mas->dev, "force suspend done\n");
	mutex_unlock(&mas->spi_ssr.ssr_lock);

	return ret;
}

static int ssr_spi_force_resume(struct device *dev)
{
	struct spi_master *spi = get_spi_master(dev);
	struct spi_geni_master *mas = spi_master_get_devdata(spi);

	mutex_lock(&mas->spi_ssr.ssr_lock);
	mas->spi_ssr.is_ssr_down = false;
	enable_irq(mas->irq);
	GENI_SE_DBG(mas->ipc, false, mas->dev, "force resume done\n");
	mutex_unlock(&mas->spi_ssr.ssr_lock);

	return 0;
}

static const struct dev_pm_ops spi_geni_pm_ops = {
	SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
					spi_geni_runtime_resume, NULL)
+41 −2
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@
#include <linux/list.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
/* SSC Qup SSR related */
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/scm.h>

/* Transfer mode supported by GENI Serial Engines */
enum se_xfer_mode {
@@ -39,8 +42,43 @@ enum se_protocol_types {
	SPI_SLAVE
};

/* Notifier block Structure */
struct ssc_qup_nb {
	struct notifier_block nb;
	void *next; /*Notifier block pointer to next notifier block structure*/
};

/**
 * struct ssc_qup_ssr	GENI Serial Engine SSC qup SSR Structure.
 * @probe_completed	To ignore up notification during probe.
 * @is_ssr_down	To check SE status.
 * @subsys_name	Subsystem name for ssr registration.
 * @active_list_head	List Head of all client in SSC QUPv3.
 */
struct ssc_qup_ssr {
	struct ssc_qup_nb ssc_qup_nb;
	bool probe_completed;
	bool is_ssr_down;
	const char *subsys_name;
	struct list_head active_list_head;
};

/**
 * struct se_rsc_ssr	GENI Resource SSR Structure.
 * @active_list	List of SSC qup SE clients.
 * @force_suspend	Function pointer for Subsystem shutdown case.
 * @force_resume	Function pointer for Subsystem restart case.
 * @ssr_enable		To check SSC Qup SSR enable status.
 */
struct se_rsc_ssr {
	struct list_head active_list;
	int (*force_suspend)(struct device *ctrl_dev);
	int (*force_resume)(struct device *ctrl_dev);
	bool ssr_enable;
};

/**
 * struct geni_se_rsc - GENI Serial Engine Resource
 * struct se_geni_rsc - GENI Serial Engine Resource
 * @ctrl_dev		Pointer to controller device.
 * @wrapper_dev:	Pointer to the parent QUPv3 core.
 * @se_clk:		Handle to the core serial engine clock.
@@ -79,6 +117,7 @@ struct se_geni_rsc {
	struct pinctrl_state *geni_gpio_active;
	struct pinctrl_state *geni_gpio_sleep;
	int clk_freq_out;
	struct se_rsc_ssr rsc_ssr;
};

#define PINCTRL_DEFAULT	"default"
Loading