Loading Documentation/devicetree/bindings/platform/msm/qcom-geni-se.txt +2 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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>; Loading Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading drivers/platform/msm/qcom-geni-se.c +129 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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) Loading @@ -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); Loading drivers/spi/spi-geni-qcom.c +133 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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) { Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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) { Loading @@ -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; Loading @@ -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)) Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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) Loading include/linux/qcom-geni-se.h +41 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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. Loading Loading @@ -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 Loading
Documentation/devicetree/bindings/platform/msm/qcom-geni-se.txt +2 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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>; Loading
Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
drivers/platform/msm/qcom-geni-se.c +129 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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) Loading @@ -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); Loading
drivers/spi/spi-geni-qcom.c +133 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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) { Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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) { Loading @@ -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; Loading @@ -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)) Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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, Loading Loading @@ -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) Loading Loading @@ -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) Loading
include/linux/qcom-geni-se.h +41 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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. Loading Loading @@ -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