From a9ccc123a8e05365f7515dcd023b01100809a6fa Mon Sep 17 00:00:00 2001 From: Vince Hsu Date: Thu, 11 Aug 2016 09:13:36 +0800 Subject: soc/tegra: pmc: Fix incorrect DPD request Reading the DPD_REQ & DPD2_REQ registers returns the previous requests. If we sets the current request bit with the returned value, then other pads will be turned on or off unexpectedly. Signed-off-by: Vince Hsu Reviewed-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 71c834f..7792ed8 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -967,8 +967,8 @@ static void tegra_io_rail_unprepare(void) int tegra_io_rail_power_on(unsigned int id) { - unsigned long request, status, value; - unsigned int bit, mask; + unsigned long request, status; + unsigned int bit; int err; mutex_lock(&pmc->powergates_lock); @@ -977,15 +977,9 @@ int tegra_io_rail_power_on(unsigned int id) if (err) goto error; - mask = 1 << bit; + tegra_pmc_writel(IO_DPD_REQ_CODE_OFF | BIT(bit), request); - value = tegra_pmc_readl(request); - value |= mask; - value &= ~IO_DPD_REQ_CODE_MASK; - value |= IO_DPD_REQ_CODE_OFF; - tegra_pmc_writel(value, request); - - err = tegra_io_rail_poll(status, mask, 0, 250); + err = tegra_io_rail_poll(status, BIT(bit), 0, 250); if (err) { pr_info("tegra_io_rail_poll() failed: %d\n", err); goto error; @@ -1002,8 +996,8 @@ EXPORT_SYMBOL(tegra_io_rail_power_on); int tegra_io_rail_power_off(unsigned int id) { - unsigned long request, status, value; - unsigned int bit, mask; + unsigned long request, status; + unsigned int bit; int err; mutex_lock(&pmc->powergates_lock); @@ -1014,15 +1008,9 @@ int tegra_io_rail_power_off(unsigned int id) goto error; } - mask = 1 << bit; - - value = tegra_pmc_readl(request); - value |= mask; - value &= ~IO_DPD_REQ_CODE_MASK; - value |= IO_DPD_REQ_CODE_ON; - tegra_pmc_writel(value, request); + tegra_pmc_writel(IO_DPD_REQ_CODE_ON | BIT(bit), request); - err = tegra_io_rail_poll(status, mask, mask, 250); + err = tegra_io_rail_poll(status, BIT(bit), BIT(bit), 250); if (err) goto error; -- cgit v1.1 From 6f27ab3e424a9532fd8e36c8732abb38eb0da993 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Thu, 18 Aug 2016 18:24:39 +0800 Subject: soc: rockchip: support active_wakeup for rockchip power-domains Register gpd_dev_ops.active_wakeup function to support keep power during suspend state. And add flag to each power domain to decide whether keep power during suspend or not. Signed-off-by: Elaine Zhang Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/pm_domains.c | 100 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 43 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 44842a2..7acd151 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -27,6 +27,7 @@ struct rockchip_domain_info { int req_mask; int idle_mask; int ack_mask; + bool active_wakeup; }; struct rockchip_pmu_info { @@ -75,23 +76,24 @@ struct rockchip_pmu { #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd) -#define DOMAIN(pwr, status, req, idle, ack) \ +#define DOMAIN(pwr, status, req, idle, ack, wakeup) \ { \ .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \ .status_mask = (status >= 0) ? BIT(status) : 0, \ .req_mask = (req >= 0) ? BIT(req) : 0, \ .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ + .active_wakeup = wakeup, \ } -#define DOMAIN_RK3288(pwr, status, req) \ - DOMAIN(pwr, status, req, req, (req) + 16) +#define DOMAIN_RK3288(pwr, status, req, wakeup) \ + DOMAIN(pwr, status, req, req, (req) + 16, wakeup) -#define DOMAIN_RK3368(pwr, status, req) \ - DOMAIN(pwr, status, req, (req) + 16, req) +#define DOMAIN_RK3368(pwr, status, req, wakeup) \ + DOMAIN(pwr, status, req, (req) + 16, req, wakeup) -#define DOMAIN_RK3399(pwr, status, req) \ - DOMAIN(pwr, status, req, req, req) +#define DOMAIN_RK3399(pwr, status, req, wakeup) \ + DOMAIN(pwr, status, req, req, req, wakeup) static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd) { @@ -295,6 +297,17 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd, pm_clk_destroy(dev); } +static bool rockchip_active_wakeup(struct device *dev) +{ + struct generic_pm_domain *genpd; + struct rockchip_pm_domain *pd; + + genpd = pd_to_genpd(dev->pm_domain); + pd = container_of(genpd, struct rockchip_pm_domain, genpd); + + return pd->info->active_wakeup; +} + static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, struct device_node *node) { @@ -415,6 +428,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, pd->genpd.power_on = rockchip_pd_power_on; pd->genpd.attach_dev = rockchip_pd_attach_dev; pd->genpd.detach_dev = rockchip_pd_detach_dev; + pd->genpd.dev_ops.active_wakeup = rockchip_active_wakeup; pd->genpd.flags = GENPD_FLAG_PM_CLK; pm_genpd_init(&pd->genpd, NULL, false); @@ -623,48 +637,48 @@ err_out: } static const struct rockchip_domain_info rk3288_pm_domains[] = { - [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4), - [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9), - [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3), - [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2), + [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4, false), + [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9, false), + [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3, false), + [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2, false), }; static const struct rockchip_domain_info rk3368_pm_domains[] = { - [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6), - [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8), - [RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7), - [RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2), - [RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2), + [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6, true), + [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8, false), + [RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7, false), + [RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2, false), + [RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2, false), }; static const struct rockchip_domain_info rk3399_pm_domains[] = { - [RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1), - [RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1), - [RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1), - [RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15), - [RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16), - [RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1), - [RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2), - [RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14), - [RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17), - [RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0), - [RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3), - [RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4), - [RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5), - [RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6), - [RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1), - [RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7), - [RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8), - [RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9), - [RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10), - [RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11), - [RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23), - [RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24), - [RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12), - [RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22), - [RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27), - [RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28), - [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29), + [RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1, false), + [RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1, false), + [RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1, true), + [RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15, true), + [RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16, true), + [RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1, true), + [RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2, true), + [RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14, true), + [RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17, false), + [RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0, false), + [RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3, false), + [RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4, false), + [RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5, false), + [RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6, false), + [RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1, false), + [RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7, false), + [RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8, false), + [RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9, false), + [RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10, false), + [RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11, false), + [RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23, true), + [RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24, true), + [RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12, true), + [RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22, false), + [RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27, true), + [RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28, true), + [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29, true), }; static const struct rockchip_pmu_info rk3288_pmu = { -- cgit v1.1 From e180f887ba40a916153e29e6ad48c34d28966740 Mon Sep 17 00:00:00 2001 From: Henry Chen Date: Wed, 13 Jul 2016 11:34:28 +0800 Subject: soc: mediatek: PMIC wrap: Extend the waiting time to 10ms. Read data fails sometimes because of a timeout that PMIC cannot transfer data to PMIC wrap on time, extend the waiting time to 10ms to reduce the failed rate. Signed-off-by: Henry Chen Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index a003ba2..a5f1093 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -583,7 +583,7 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp, { unsigned long timeout; - timeout = jiffies + usecs_to_jiffies(255); + timeout = jiffies + usecs_to_jiffies(10000); do { if (time_after(jiffies, timeout)) -- cgit v1.1 From 951a5af92276f5b2f1b8834bd5eef96d8788f66b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 Jul 2016 14:18:59 -0700 Subject: soc: qcom: smem: Silence probe defer error If we fail to get the hwspinlock due to probe defer, we shouldn't print an error message. Just be silent in this case. Cc: Bjorn Andersson Signed-off-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 2e1aa9f..18ec52f 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -740,7 +740,8 @@ static int qcom_smem_probe(struct platform_device *pdev) hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); if (hwlock_id < 0) { - dev_err(&pdev->dev, "failed to retrieve hwlock\n"); + if (hwlock_id != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to retrieve hwlock\n"); return hwlock_id; } -- cgit v1.1 From 0a0c08cae01b33b29abd24608d3800986546f0af Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 9 Aug 2016 17:39:19 -0700 Subject: soc: qcom: smd: Simplify multi channel handling Multi-channel clients split between several drivers need a way to close individual channels, as these drivers might be removed individually. With this in place the responsibility of closing additionally opened channels to the client as well only concerning smd about the primary channel. With this approach we will only trigger removal of SMD devices based on the state of the primary channel, however we get in sync with how rpmsg works. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smd.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index ac1957d..63e72eb 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c @@ -197,7 +197,6 @@ struct qcom_smd_channel { void *drvdata; struct list_head list; - struct list_head dev_list; }; /** @@ -891,8 +890,6 @@ static int qcom_smd_dev_remove(struct device *dev) struct qcom_smd_device *qsdev = to_smd_device(dev); struct qcom_smd_driver *qsdrv = to_smd_driver(dev); struct qcom_smd_channel *channel = qsdev->channel; - struct qcom_smd_channel *tmp; - struct qcom_smd_channel *ch; qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING); @@ -911,15 +908,9 @@ static int qcom_smd_dev_remove(struct device *dev) if (qsdrv->remove) qsdrv->remove(qsdev); - /* - * The client is now gone, close and release all channels associated - * with this sdev - */ - list_for_each_entry_safe(ch, tmp, &channel->dev_list, dev_list) { - qcom_smd_channel_close(ch); - list_del(&ch->dev_list); - ch->qsdev = NULL; - } + /* The client is now gone, close the primary channel */ + qcom_smd_channel_close(channel); + channel->qsdev = NULL; return 0; } @@ -1091,6 +1082,8 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) * * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't * ready. + * + * Any channels returned must be closed with a call to qcom_smd_close_channel() */ struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent, const char *name, @@ -1120,15 +1113,21 @@ struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent, return ERR_PTR(ret); } - /* - * Append the list of channel to the channels associated with the sdev - */ - list_add_tail(&channel->dev_list, &sdev->channel->dev_list); - return channel; } EXPORT_SYMBOL(qcom_smd_open_channel); +/** + * qcom_smd_close_channel() - close an additionally opened channel + * @channel: channel handle, returned by qcom_smd_open_channel() + */ +void qcom_smd_close_channel(struct qcom_smd_channel *channel) +{ + qcom_smd_channel_close(channel); + channel->qsdev = NULL; +} +EXPORT_SYMBOL(qcom_smd_close_channel); + /* * Allocate the qcom_smd_channel object for a newly found smd channel, * retrieving and validating the smem items involved. @@ -1150,7 +1149,6 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed if (!channel) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&channel->dev_list); channel->edge = edge; channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); if (!channel->name) -- cgit v1.1 From 381a0b4ce45b2ad809b79049e6316a83d5eaa2ea Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 9 Aug 2016 17:42:55 -0700 Subject: soc: qcom: smd: Request irqs after parsing properties The code exectued by the interrupt handler depends on the values parsed after requesting the irq, just to be save we should therefor move the request_irq() call to be done after parsing the properties. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smd.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index 63e72eb..679f777 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c @@ -1348,22 +1348,6 @@ static int qcom_smd_parse_edge(struct device *dev, edge->of_node = of_node_get(node); - irq = irq_of_parse_and_map(node, 0); - if (irq < 0) { - dev_err(dev, "required smd interrupt missing\n"); - return -EINVAL; - } - - ret = devm_request_irq(dev, irq, - qcom_smd_edge_intr, IRQF_TRIGGER_RISING, - node->name, edge); - if (ret) { - dev_err(dev, "failed to request smd irq\n"); - return ret; - } - - edge->irq = irq; - key = "qcom,smd-edge"; ret = of_property_read_u32(node, key, &edge->edge_id); if (ret) { @@ -1398,6 +1382,22 @@ static int qcom_smd_parse_edge(struct device *dev, return -EINVAL; } + irq = irq_of_parse_and_map(node, 0); + if (irq < 0) { + dev_err(dev, "required smd interrupt missing\n"); + return -EINVAL; + } + + ret = devm_request_irq(dev, irq, + qcom_smd_edge_intr, IRQF_TRIGGER_RISING, + node->name, edge); + if (ret) { + dev_err(dev, "failed to request smd irq\n"); + return ret; + } + + edge->irq = irq; + return 0; } -- cgit v1.1 From da0573026c2d3d445c39385024bfc3ce6beebe09 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 15 Aug 2016 11:15:57 -0700 Subject: soc: qcom: smd: Represent smd edges as devices By representing each edge as its own device the channels are no longer tied to being parented by the same smd device and as such an edge can live as children of e.g. remoteproc instances. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smd.c | 197 ++++++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 75 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index 679f777..f20816b 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c @@ -95,7 +95,7 @@ static const struct { /** * struct qcom_smd_edge - representing a remote processor - * @smd: handle to qcom_smd + * @dev: device for this edge * @of_node: of_node handle for information related to this edge * @edge_id: identifier of this edge * @remote_pid: identifier of remote processor @@ -111,7 +111,8 @@ static const struct { * @state_work: work item for edge state changes */ struct qcom_smd_edge { - struct qcom_smd *smd; + struct device dev; + struct device_node *of_node; unsigned edge_id; unsigned remote_pid; @@ -135,6 +136,8 @@ struct qcom_smd_edge { struct work_struct state_work; }; +#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev) + /* * SMD channel states. */ @@ -199,19 +202,6 @@ struct qcom_smd_channel { struct list_head list; }; -/** - * struct qcom_smd - smd struct - * @dev: device struct - * @num_edges: number of entries in @edges - * @edges: array of edges to be handled - */ -struct qcom_smd { - struct device *dev; - - unsigned num_edges; - struct qcom_smd_edge edges[0]; -}; - /* * Format of the smd_info smem items, for byte aligned channels. */ @@ -420,7 +410,7 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, if (channel->state == state) return; - dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state); + dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state); SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); @@ -964,13 +954,12 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) struct qcom_smd_device *qsdev; struct qcom_smd_edge *edge = channel->edge; struct device_node *node; - struct qcom_smd *smd = edge->smd; int ret; if (channel->qsdev) return -EEXIST; - dev_dbg(smd->dev, "registering '%s'\n", channel->name); + dev_dbg(&edge->dev, "registering '%s'\n", channel->name); qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); if (!qsdev) @@ -981,7 +970,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) edge->of_node->name, node ? node->name : channel->name); - qsdev->dev.parent = smd->dev; + qsdev->dev.parent = &edge->dev; qsdev->dev.bus = &qcom_smd_bus; qsdev->dev.release = qcom_smd_release_device; qsdev->dev.of_node = node; @@ -992,7 +981,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) ret = device_register(&qsdev->dev); if (ret) { - dev_err(smd->dev, "device_register failed: %d\n", ret); + dev_err(&edge->dev, "device_register failed: %d\n", ret); put_device(&qsdev->dev); } @@ -1138,19 +1127,18 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed char *name) { struct qcom_smd_channel *channel; - struct qcom_smd *smd = edge->smd; size_t fifo_size; size_t info_size; void *fifo_base; void *info; int ret; - channel = devm_kzalloc(smd->dev, sizeof(*channel), GFP_KERNEL); + channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL); if (!channel) return ERR_PTR(-ENOMEM); channel->edge = edge; - channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); + channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL); if (!channel->name) return ERR_PTR(-ENOMEM); @@ -1173,7 +1161,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed } else if (info_size == 2 * sizeof(struct smd_channel_info)) { channel->info = info; } else { - dev_err(smd->dev, + dev_err(&edge->dev, "channel info of size %zu not supported\n", info_size); ret = -EINVAL; goto free_name_and_channel; @@ -1188,7 +1176,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed /* The channel consist of a rx and tx fifo of equal size */ fifo_size /= 2; - dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", + dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", name, info_size, fifo_size); channel->tx_fifo = fifo_base; @@ -1200,8 +1188,8 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed return channel; free_name_and_channel: - devm_kfree(smd->dev, channel->name); - devm_kfree(smd->dev, channel); + devm_kfree(&edge->dev, channel->name); + devm_kfree(&edge->dev, channel); return ERR_PTR(ret); } @@ -1217,7 +1205,6 @@ static void qcom_channel_scan_worker(struct work_struct *work) struct qcom_smd_alloc_entry *alloc_tbl; struct qcom_smd_alloc_entry *entry; struct qcom_smd_channel *channel; - struct qcom_smd *smd = edge->smd; unsigned long flags; unsigned fifo_id; unsigned info_id; @@ -1261,7 +1248,7 @@ static void qcom_channel_scan_worker(struct work_struct *work) list_add(&channel->list, &edge->channels); spin_unlock_irqrestore(&edge->channels_lock, flags); - dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); + dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name); set_bit(i, edge->allocated[tbl]); wake_up_interruptible(&edge->new_channel_event); @@ -1401,15 +1388,102 @@ static int qcom_smd_parse_edge(struct device *dev, return 0; } -static int qcom_smd_probe(struct platform_device *pdev) +/* + * Release function for an edge. + * Reset the state of each associated channel and free the edge context. + */ +static void qcom_smd_edge_release(struct device *dev) +{ + struct qcom_smd_channel *channel; + struct qcom_smd_edge *edge = to_smd_edge(dev); + + list_for_each_entry(channel, &edge->channels, list) { + SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED); + SET_RX_CHANNEL_INFO(channel, head, 0); + SET_RX_CHANNEL_INFO(channel, tail, 0); + } + + kfree(edge); +} + +/** + * qcom_smd_register_edge() - register an edge based on an device_node + * @parent: parent device for the edge + * @node: device_node describing the edge + * + * Returns an edge reference, or negative ERR_PTR() on failure. + */ +struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, + struct device_node *node) { struct qcom_smd_edge *edge; - struct device_node *node; - struct qcom_smd *smd; - size_t array_size; - int num_edges; int ret; - int i = 0; + + edge = kzalloc(sizeof(*edge), GFP_KERNEL); + if (!edge) + return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&edge->new_channel_event); + + edge->dev.parent = parent; + edge->dev.release = qcom_smd_edge_release; + dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); + ret = device_register(&edge->dev); + if (ret) { + pr_err("failed to register smd edge\n"); + return ERR_PTR(ret); + } + + ret = qcom_smd_parse_edge(&edge->dev, node, edge); + if (ret) { + dev_err(&edge->dev, "failed to parse smd edge\n"); + goto unregister_dev; + } + + schedule_work(&edge->scan_work); + + return edge; + +unregister_dev: + put_device(&edge->dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(qcom_smd_register_edge); + +static int qcom_smd_remove_device(struct device *dev, void *data) +{ + device_unregister(dev); + of_node_put(dev->of_node); + put_device(dev); + + return 0; +} + +/** + * qcom_smd_unregister_edge() - release an edge and its children + * @edge: edge reference acquired from qcom_smd_register_edge + */ +int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) +{ + int ret; + + disable_irq(edge->irq); + cancel_work_sync(&edge->scan_work); + cancel_work_sync(&edge->state_work); + + ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device); + if (ret) + dev_warn(&edge->dev, "can't remove smd device: %d\n", ret); + + device_unregister(&edge->dev); + + return 0; +} +EXPORT_SYMBOL(qcom_smd_unregister_edge); + +static int qcom_smd_probe(struct platform_device *pdev) +{ + struct device_node *node; void *p; /* Wait for smem */ @@ -1417,29 +1491,17 @@ static int qcom_smd_probe(struct platform_device *pdev) if (PTR_ERR(p) == -EPROBE_DEFER) return PTR_ERR(p); - num_edges = of_get_available_child_count(pdev->dev.of_node); - array_size = sizeof(*smd) + num_edges * sizeof(struct qcom_smd_edge); - smd = devm_kzalloc(&pdev->dev, array_size, GFP_KERNEL); - if (!smd) - return -ENOMEM; - smd->dev = &pdev->dev; + for_each_available_child_of_node(pdev->dev.of_node, node) + qcom_smd_register_edge(&pdev->dev, node); - smd->num_edges = num_edges; - for_each_available_child_of_node(pdev->dev.of_node, node) { - edge = &smd->edges[i++]; - edge->smd = smd; - init_waitqueue_head(&edge->new_channel_event); - - ret = qcom_smd_parse_edge(&pdev->dev, node, edge); - if (ret) - continue; - - schedule_work(&edge->scan_work); - } + return 0; +} - platform_set_drvdata(pdev, smd); +static int qcom_smd_remove_edge(struct device *dev, void *data) +{ + struct qcom_smd_edge *edge = to_smd_edge(dev); - return 0; + return qcom_smd_unregister_edge(edge); } /* @@ -1448,28 +1510,13 @@ static int qcom_smd_probe(struct platform_device *pdev) */ static int qcom_smd_remove(struct platform_device *pdev) { - struct qcom_smd_channel *channel; - struct qcom_smd_edge *edge; - struct qcom_smd *smd = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < smd->num_edges; i++) { - edge = &smd->edges[i]; - - disable_irq(edge->irq); - cancel_work_sync(&edge->scan_work); - cancel_work_sync(&edge->state_work); - - /* No need to lock here, because the writer is gone */ - list_for_each_entry(channel, &edge->channels, list) { - if (!channel->qsdev) - continue; + int ret; - qcom_smd_destroy_device(channel); - } - } + ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge); + if (ret) + dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret); - return 0; + return ret; } static const struct of_device_id qcom_smd_of_match[] = { -- cgit v1.1 From 4e21a95d7fbc526d35d860ec34c468e04f53672f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 26 Aug 2016 14:59:17 -0700 Subject: soc: qcom: smd: Reset rx tail rather than tx The local end of each SMD channel is responsible for updating the tx head and the rx tail, as such we should not touch the tx tail during a reset. Reported-by: Jeremy McNicoll Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c index f20816b..322034a 100644 --- a/drivers/soc/qcom/smd.c +++ b/drivers/soc/qcom/smd.c @@ -363,7 +363,7 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1); SET_TX_CHANNEL_INFO(channel, head, 0); - SET_TX_CHANNEL_INFO(channel, tail, 0); + SET_RX_CHANNEL_INFO(channel, tail, 0); qcom_smd_signal_channel(channel); -- cgit v1.1