From 39a85bcbfc54d602934e2657c146c299d71b27ba Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 11 Feb 2014 09:38:53 +0000 Subject: mfd: omap-usb-tll: Fix cppcheck sizeof warning Static analysis from cppcheck issued the following warning: [drivers/mfd/omap-usb-tll.c:255]: (warning) Found calculation inside sizeof(). The current size calculation is not obvious and is easy to miscomprehend, so re-work the size of the allocation based on the size of the struct pointer and quantity to allocate. Signed-off-by: Colin Ian King Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-tll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 5ee50f779ef6..532eacab6b46 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -252,7 +252,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) break; } - tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), + tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch, GFP_KERNEL); if (!tll->ch_clk) { ret = -ENOMEM; -- cgit v1.2.3 From 61b7025f6d6cebc9a8ebbe020c4de5a76a536c90 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 12 Feb 2014 12:18:42 +0200 Subject: mfd: omap-usb-host: Use resource managed clk_get() Use devm_clk_get() instead of clk_get(). Signed-off-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-host.c | 81 +++++++++------------------------------------ 1 file changed, 16 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 90b630ccc8bc..0c3c9a0c7638 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -674,46 +674,46 @@ static int usbhs_omap_probe(struct platform_device *pdev) omap->ehci_logic_fck = ERR_PTR(-EINVAL); if (need_logic_fck) { - omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); + omap->ehci_logic_fck = devm_clk_get(dev, "ehci_logic_fck"); if (IS_ERR(omap->ehci_logic_fck)) { ret = PTR_ERR(omap->ehci_logic_fck); dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); } } - omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk"); + omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk"); if (IS_ERR(omap->utmi_p1_gfclk)) { ret = PTR_ERR(omap->utmi_p1_gfclk); dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); - goto err_p1_gfclk; + goto err_mem; } - omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk"); + omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk"); if (IS_ERR(omap->utmi_p2_gfclk)) { ret = PTR_ERR(omap->utmi_p2_gfclk); dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); - goto err_p2_gfclk; + goto err_mem; } - omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); + omap->xclk60mhsp1_ck = devm_clk_get(dev, "xclk60mhsp1_ck"); if (IS_ERR(omap->xclk60mhsp1_ck)) { ret = PTR_ERR(omap->xclk60mhsp1_ck); dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); - goto err_xclk60mhsp1; + goto err_mem; } - omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); + omap->xclk60mhsp2_ck = devm_clk_get(dev, "xclk60mhsp2_ck"); if (IS_ERR(omap->xclk60mhsp2_ck)) { ret = PTR_ERR(omap->xclk60mhsp2_ck); dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); - goto err_xclk60mhsp2; + goto err_mem; } - omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); + omap->init_60m_fclk = devm_clk_get(dev, "init_60m_fclk"); if (IS_ERR(omap->init_60m_fclk)) { ret = PTR_ERR(omap->init_60m_fclk); dev_err(dev, "init_60m_fclk failed error:%d\n", ret); - goto err_init60m; + goto err_mem; } for (i = 0; i < omap->nports; i++) { @@ -727,21 +727,21 @@ static int usbhs_omap_probe(struct platform_device *pdev) * platforms have all clocks and we can function without * them */ - omap->utmi_clk[i] = clk_get(dev, clkname); + omap->utmi_clk[i] = devm_clk_get(dev, clkname); if (IS_ERR(omap->utmi_clk[i])) dev_dbg(dev, "Failed to get clock : %s : %ld\n", clkname, PTR_ERR(omap->utmi_clk[i])); snprintf(clkname, sizeof(clkname), "usb_host_hs_hsic480m_p%d_clk", i + 1); - omap->hsic480m_clk[i] = clk_get(dev, clkname); + omap->hsic480m_clk[i] = devm_clk_get(dev, clkname); if (IS_ERR(omap->hsic480m_clk[i])) dev_dbg(dev, "Failed to get clock : %s : %ld\n", clkname, PTR_ERR(omap->hsic480m_clk[i])); snprintf(clkname, sizeof(clkname), "usb_host_hs_hsic60m_p%d_clk", i + 1); - omap->hsic60m_clk[i] = clk_get(dev, clkname); + omap->hsic60m_clk[i] = devm_clk_get(dev, clkname); if (IS_ERR(omap->hsic60m_clk[i])) dev_dbg(dev, "Failed to get clock : %s : %ld\n", clkname, PTR_ERR(omap->hsic60m_clk[i])); @@ -784,7 +784,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "Failed to create DT children: %d\n", ret); - goto err_alloc; + goto err_mem; } } else { @@ -792,40 +792,12 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "omap_usbhs_alloc_children failed: %d\n", ret); - goto err_alloc; + goto err_mem; } } return 0; -err_alloc: - for (i = 0; i < omap->nports; i++) { - if (!IS_ERR(omap->utmi_clk[i])) - clk_put(omap->utmi_clk[i]); - if (!IS_ERR(omap->hsic60m_clk[i])) - clk_put(omap->hsic60m_clk[i]); - if (!IS_ERR(omap->hsic480m_clk[i])) - clk_put(omap->hsic480m_clk[i]); - } - - clk_put(omap->init_60m_fclk); - -err_init60m: - clk_put(omap->xclk60mhsp2_ck); - -err_xclk60mhsp2: - clk_put(omap->xclk60mhsp1_ck); - -err_xclk60mhsp1: - clk_put(omap->utmi_p2_gfclk); - -err_p2_gfclk: - clk_put(omap->utmi_p1_gfclk); - -err_p1_gfclk: - if (!IS_ERR(omap->ehci_logic_fck)) - clk_put(omap->ehci_logic_fck); - err_mem: pm_runtime_disable(dev); @@ -847,27 +819,6 @@ static int usbhs_omap_remove_child(struct device *dev, void *data) */ static int usbhs_omap_remove(struct platform_device *pdev) { - struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < omap->nports; i++) { - if (!IS_ERR(omap->utmi_clk[i])) - clk_put(omap->utmi_clk[i]); - if (!IS_ERR(omap->hsic60m_clk[i])) - clk_put(omap->hsic60m_clk[i]); - if (!IS_ERR(omap->hsic480m_clk[i])) - clk_put(omap->hsic480m_clk[i]); - } - - clk_put(omap->init_60m_fclk); - clk_put(omap->utmi_p1_gfclk); - clk_put(omap->utmi_p2_gfclk); - clk_put(omap->xclk60mhsp2_ck); - clk_put(omap->xclk60mhsp1_ck); - - if (!IS_ERR(omap->ehci_logic_fck)) - clk_put(omap->ehci_logic_fck); - pm_runtime_disable(&pdev->dev); /* remove children */ -- cgit v1.2.3 From 3aca446acf32243029f5c83810b50aad3c32b6bf Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 27 Feb 2014 16:18:23 +0200 Subject: mfd: omap-usb-host: Get clocks based on hardware revision Not all revisions have all the clocks so get the necessary clocks based on hardware revision. This should avoid un-necessary clk_get failure messages that were observed earlier. Also remove the dummy USB host clocks from the OMAP3 clock data. These are no longer expected by the driver. Acked-by: Mike Turquette [OMAP3 CLK data] Acked-by: Tony Lindgren Signed-off-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/clk/ti/clk-3xxx.c | 4 ---- drivers/mfd/omap-usb-host.c | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index d3230234f07b..0d1750a8aea4 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -130,10 +130,6 @@ static struct ti_dt_clk omap3xxx_clks[] = { DT_CLK(NULL, "dss_tv_fck", "dss_tv_fck"), DT_CLK(NULL, "dss_96m_fck", "dss_96m_fck"), DT_CLK(NULL, "dss2_alwon_fck", "dss2_alwon_fck"), - DT_CLK(NULL, "utmi_p1_gfclk", "dummy_ck"), - DT_CLK(NULL, "utmi_p2_gfclk", "dummy_ck"), - DT_CLK(NULL, "xclk60mhsp1_ck", "dummy_ck"), - DT_CLK(NULL, "xclk60mhsp2_ck", "dummy_ck"), DT_CLK(NULL, "init_60m_fclk", "dummy_ck"), DT_CLK(NULL, "gpt1_fck", "gpt1_fck"), DT_CLK(NULL, "aes2_ick", "aes2_ick"), diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 0c3c9a0c7638..c63bfdf5a419 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -665,22 +665,43 @@ static int usbhs_omap_probe(struct platform_device *pdev) goto err_mem; } - need_logic_fck = false; + /* Set all clocks as invalid to begin with */ + omap->ehci_logic_fck = ERR_PTR(-ENODEV); + omap->init_60m_fclk = ERR_PTR(-ENODEV); + omap->utmi_p1_gfclk = ERR_PTR(-ENODEV); + omap->utmi_p2_gfclk = ERR_PTR(-ENODEV); + omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV); + omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV); + for (i = 0; i < omap->nports; i++) { - if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || - is_ehci_hsic_mode(i)) - need_logic_fck |= true; + omap->utmi_clk[i] = ERR_PTR(-ENODEV); + omap->hsic480m_clk[i] = ERR_PTR(-ENODEV); + omap->hsic60m_clk[i] = ERR_PTR(-ENODEV); } - omap->ehci_logic_fck = ERR_PTR(-EINVAL); - if (need_logic_fck) { - omap->ehci_logic_fck = devm_clk_get(dev, "ehci_logic_fck"); - if (IS_ERR(omap->ehci_logic_fck)) { - ret = PTR_ERR(omap->ehci_logic_fck); - dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); + /* for OMAP3 i.e. USBHS REV1 */ + if (omap->usbhs_rev == OMAP_USBHS_REV1) { + need_logic_fck = false; + for (i = 0; i < omap->nports; i++) { + if (is_ehci_phy_mode(pdata->port_mode[i]) || + is_ehci_tll_mode(pdata->port_mode[i]) || + is_ehci_hsic_mode(pdata->port_mode[i])) + + need_logic_fck |= true; + } + + if (need_logic_fck) { + omap->ehci_logic_fck = devm_clk_get(dev, + "ehci_logic_fck"); + if (IS_ERR(omap->ehci_logic_fck)) { + ret = PTR_ERR(omap->ehci_logic_fck); + dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); + } } + goto initialize; } + /* for OMAP4+ i.e. USBHS REV2+ */ omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk"); if (IS_ERR(omap->utmi_p1_gfclk)) { ret = PTR_ERR(omap->utmi_p1_gfclk); @@ -748,7 +769,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) } if (is_ehci_phy_mode(pdata->port_mode[0])) { - /* for OMAP3, clk_set_parent fails */ ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); if (ret != 0) @@ -776,6 +796,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) ret); } +initialize: omap_usbhs_init(dev); if (dev->of_node) { -- cgit v1.2.3 From fedb2e7c2d7b80dfda6d906f665ff01f368e7b51 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 27 Feb 2014 16:18:24 +0200 Subject: mfd: omap-usb-host: Always fail on clk_get() error Be more strict and always fail on clk_get() error. Acked-by: Tony Lindgren Signed-off-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-host.c | 62 +++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index c63bfdf5a419..c31baa743986 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -695,7 +695,8 @@ static int usbhs_omap_probe(struct platform_device *pdev) "ehci_logic_fck"); if (IS_ERR(omap->ehci_logic_fck)) { ret = PTR_ERR(omap->ehci_logic_fck); - dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); + dev_err(dev, "ehci_logic_fck failed:%d\n", ret); + goto err_mem; } } goto initialize; @@ -749,51 +750,68 @@ static int usbhs_omap_probe(struct platform_device *pdev) * them */ omap->utmi_clk[i] = devm_clk_get(dev, clkname); - if (IS_ERR(omap->utmi_clk[i])) - dev_dbg(dev, "Failed to get clock : %s : %ld\n", - clkname, PTR_ERR(omap->utmi_clk[i])); + if (IS_ERR(omap->utmi_clk[i])) { + ret = PTR_ERR(omap->utmi_clk[i]); + dev_err(dev, "Failed to get clock : %s : %d\n", + clkname, ret); + goto err_mem; + } snprintf(clkname, sizeof(clkname), "usb_host_hs_hsic480m_p%d_clk", i + 1); omap->hsic480m_clk[i] = devm_clk_get(dev, clkname); - if (IS_ERR(omap->hsic480m_clk[i])) - dev_dbg(dev, "Failed to get clock : %s : %ld\n", - clkname, PTR_ERR(omap->hsic480m_clk[i])); + if (IS_ERR(omap->hsic480m_clk[i])) { + ret = PTR_ERR(omap->hsic480m_clk[i]); + dev_err(dev, "Failed to get clock : %s : %d\n", + clkname, ret); + goto err_mem; + } snprintf(clkname, sizeof(clkname), "usb_host_hs_hsic60m_p%d_clk", i + 1); omap->hsic60m_clk[i] = devm_clk_get(dev, clkname); - if (IS_ERR(omap->hsic60m_clk[i])) - dev_dbg(dev, "Failed to get clock : %s : %ld\n", - clkname, PTR_ERR(omap->hsic60m_clk[i])); + if (IS_ERR(omap->hsic60m_clk[i])) { + ret = PTR_ERR(omap->hsic60m_clk[i]); + dev_err(dev, "Failed to get clock : %s : %d\n", + clkname, ret); + goto err_mem; + } } if (is_ehci_phy_mode(pdata->port_mode[0])) { ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); - if (ret != 0) - dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n", - ret); + if (ret != 0) { + dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n", + ret); + goto err_mem; + } } else if (is_ehci_tll_mode(pdata->port_mode[0])) { ret = clk_set_parent(omap->utmi_p1_gfclk, omap->init_60m_fclk); - if (ret != 0) - dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n", - ret); + if (ret != 0) { + dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n", + ret); + goto err_mem; + } } if (is_ehci_phy_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->xclk60mhsp2_ck); - if (ret != 0) - dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n", - ret); + if (ret != 0) { + dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n", + ret); + goto err_mem; + } } else if (is_ehci_tll_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->init_60m_fclk); - if (ret != 0) - dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n", - ret); + if (ret != 0) { + dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n", + ret); + goto err_mem; + } } initialize: -- cgit v1.2.3 From 775bb078e9af9747f7d4064939e1a50195c9fb4b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 27 Feb 2014 16:18:25 +0200 Subject: mfd: omap-usb-host: Use proper clock name instead of alias Use the proper clock name 'usbhost_120m_fck' instead of the alias 'ehci_logic_fck' Get rid of the 'ehci_logic_fck' alias from the OMAP3 hwmod data as well. Acked-by: Tony Lindgren Signed-off-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-host.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index c31baa743986..865c2764a89d 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -692,10 +692,11 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (need_logic_fck) { omap->ehci_logic_fck = devm_clk_get(dev, - "ehci_logic_fck"); + "usbhost_120m_fck"); if (IS_ERR(omap->ehci_logic_fck)) { ret = PTR_ERR(omap->ehci_logic_fck); - dev_err(dev, "ehci_logic_fck failed:%d\n", ret); + dev_err(dev, "usbhost_120m_fck failed:%d\n", + ret); goto err_mem; } } -- cgit v1.2.3 From 051fc06dfaa322e1079edc476e6e2500220c562d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 27 Feb 2014 16:18:26 +0200 Subject: mfd: omap-usb-host: Use clock names as per function for reference clocks Use a meaningful name for the reference clocks so that it indicates the function. Update the OMAP4+ USB Host node as well to be in sync with the changes. Acked-by: Tony Lindgren Signed-off-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-host.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 865c2764a89d..651e249287dc 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -718,24 +718,24 @@ static int usbhs_omap_probe(struct platform_device *pdev) goto err_mem; } - omap->xclk60mhsp1_ck = devm_clk_get(dev, "xclk60mhsp1_ck"); + omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1"); if (IS_ERR(omap->xclk60mhsp1_ck)) { ret = PTR_ERR(omap->xclk60mhsp1_ck); - dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); + dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret); goto err_mem; } - omap->xclk60mhsp2_ck = devm_clk_get(dev, "xclk60mhsp2_ck"); + omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2"); if (IS_ERR(omap->xclk60mhsp2_ck)) { ret = PTR_ERR(omap->xclk60mhsp2_ck); - dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); + dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret); goto err_mem; } - omap->init_60m_fclk = devm_clk_get(dev, "init_60m_fclk"); + omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int"); if (IS_ERR(omap->init_60m_fclk)) { ret = PTR_ERR(omap->init_60m_fclk); - dev_err(dev, "init_60m_fclk failed error:%d\n", ret); + dev_err(dev, "refclk_60m_int failed error:%d\n", ret); goto err_mem; } -- cgit v1.2.3 From e7f22b75167d8b8a4d4c67d12b026a746aec05f6 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 16 Mar 2014 02:43:25 +0100 Subject: mfd: twl4030-madc: Use managed resources Update twl4030-madc driver to use managed resources. Signed-off-by: Sebastian Reichel Acked-by: Jonathan Cameron Tested-by: Marek Belisko Signed-off-by: Lee Jones --- drivers/mfd/twl4030-madc.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 4c583e471339..54585617ab51 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -702,14 +702,14 @@ static int twl4030_madc_probe(struct platform_device *pdev) { struct twl4030_madc_data *madc; struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); - int ret; + int irq, ret; u8 regval; if (!pdata) { dev_err(&pdev->dev, "platform_data not available\n"); return -EINVAL; } - madc = kzalloc(sizeof(*madc), GFP_KERNEL); + madc = devm_kzalloc(&pdev->dev, sizeof(*madc), GFP_KERNEL); if (!madc) return -ENOMEM; @@ -726,7 +726,7 @@ static int twl4030_madc_probe(struct platform_device *pdev) TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; ret = twl4030_madc_set_power(madc, 1); if (ret < 0) - goto err_power; + return ret; ret = twl4030_madc_set_current_generator(madc, 0, 1); if (ret < 0) goto err_current_generator; @@ -770,7 +770,9 @@ static int twl4030_madc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, madc); mutex_init(&madc->lock); - ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, twl4030_madc_threaded_irq_handler, IRQF_TRIGGER_RISING, "twl4030_madc", madc); if (ret) { @@ -783,9 +785,6 @@ err_i2c: twl4030_madc_set_current_generator(madc, 0, 0); err_current_generator: twl4030_madc_set_power(madc, 0); -err_power: - kfree(madc); - return ret; } @@ -793,10 +792,8 @@ static int twl4030_madc_remove(struct platform_device *pdev) { struct twl4030_madc_data *madc = platform_get_drvdata(pdev); - free_irq(platform_get_irq(pdev, 0), madc); twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_power(madc, 0); - kfree(madc); return 0; } -- cgit v1.2.3 From 2f39b70fef194a54c9decd8687bb05d576afebe5 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 16 Mar 2014 02:43:26 +0100 Subject: mfd: twl4030-madc: Add DT support and convert to IIO framework This converts twl4030-madc module to use the Industrial IO ADC framework and adds device tree support. Signed-off-by: Sebastian Reichel Tested-by: Marek Belisko Acked-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/mfd/twl4030-madc.c | 127 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 54585617ab51..93ef91229cd3 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -47,11 +47,14 @@ #include #include +#include + /* * struct twl4030_madc_data - a container for madc info * @dev - pointer to device structure for madc * @lock - mutex protecting this data structure * @requests - Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq - IRQ selection (main or co-processor) * @imr - Interrupt mask register of MADC * @isr - Interrupt status register of MADC */ @@ -59,10 +62,71 @@ struct twl4030_madc_data { struct device *dev; struct mutex lock; /* mutex protecting this data structure */ struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + bool use_second_irq; int imr; int isr; }; +static int twl4030_madc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl4030_madc_data *madc = iio_priv(iio_dev); + struct twl4030_madc_request req; + int ret; + + req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; + + req.channels = BIT(chan->channel); + req.active = false; + req.func_cb = NULL; + req.type = TWL4030_MADC_WAIT; + req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); + req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); + + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + + *val = req.rbuf[chan->channel]; + + return IIO_VAL_INT; +} + +static const struct iio_info twl4030_madc_iio_info = { + .read_raw = &twl4030_madc_read, + .driver_module = THIS_MODULE, +}; + +#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = _name, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl4030_madc_iio_channels[] = { + TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), + TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), + TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), + TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), + TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), + TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), + TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), + TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), + TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), + TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), + TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), + TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), + TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), + TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), + TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), + TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), +}; + static struct twl4030_madc_data *twl4030_madc; struct twl4030_prescale_divider_ratios { @@ -702,28 +766,49 @@ static int twl4030_madc_probe(struct platform_device *pdev) { struct twl4030_madc_data *madc; struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; int irq, ret; u8 regval; + struct iio_dev *iio_dev = NULL; - if (!pdata) { - dev_err(&pdev->dev, "platform_data not available\n"); + if (!pdata && !np) { + dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); return -EINVAL; } - madc = devm_kzalloc(&pdev->dev, sizeof(*madc), GFP_KERNEL); - if (!madc) + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); + if (!iio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); return -ENOMEM; + } + madc = iio_priv(iio_dev); madc->dev = &pdev->dev; + iio_dev->name = dev_name(&pdev->dev); + iio_dev->dev.parent = &pdev->dev; + iio_dev->dev.of_node = pdev->dev.of_node; + iio_dev->info = &twl4030_madc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = twl4030_madc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); + /* * Phoenix provides 2 interrupt lines. The first one is connected to * the OMAP. The other one can be connected to the other processor such * as modem. Hence two separate ISR and IMR registers. */ - madc->imr = (pdata->irq_line == 1) ? - TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; - madc->isr = (pdata->irq_line == 1) ? - TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; + if (pdata) + madc->use_second_irq = (pdata->irq_line != 1); + else + madc->use_second_irq = of_property_read_bool(np, + "ti,system-uses-second-madc-irq"); + + madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : + TWL4030_MADC_IMR1; + madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : + TWL4030_MADC_ISR1; + ret = twl4030_madc_set_power(madc, 1); if (ret < 0) return ret; @@ -768,7 +853,7 @@ static int twl4030_madc_probe(struct platform_device *pdev) } } - platform_set_drvdata(pdev, madc); + platform_set_drvdata(pdev, iio_dev); mutex_init(&madc->lock); irq = platform_get_irq(pdev, 0); @@ -776,11 +861,19 @@ static int twl4030_madc_probe(struct platform_device *pdev) twl4030_madc_threaded_irq_handler, IRQF_TRIGGER_RISING, "twl4030_madc", madc); if (ret) { - dev_dbg(&pdev->dev, "could not request irq\n"); + dev_err(&pdev->dev, "could not request irq\n"); goto err_i2c; } twl4030_madc = madc; + + ret = iio_device_register(iio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio device\n"); + goto err_i2c; + } + return 0; + err_i2c: twl4030_madc_set_current_generator(madc, 0, 0); err_current_generator: @@ -790,7 +883,10 @@ err_current_generator: static int twl4030_madc_remove(struct platform_device *pdev) { - struct twl4030_madc_data *madc = platform_get_drvdata(pdev); + struct iio_dev *iio_dev = platform_get_drvdata(pdev); + struct twl4030_madc_data *madc = iio_priv(iio_dev); + + iio_device_unregister(iio_dev); twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_power(madc, 0); @@ -798,12 +894,21 @@ static int twl4030_madc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id twl_madc_of_match[] = { + { .compatible = "ti,twl4030-madc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_madc_of_match); +#endif + static struct platform_driver twl4030_madc_driver = { .probe = twl4030_madc_probe, .remove = twl4030_madc_remove, .driver = { .name = "twl4030_madc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl_madc_of_match), }, }; -- cgit v1.2.3 From 99be0245c8869cbd7b9c0ab3f093f41c60362f91 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 16 Mar 2014 02:43:27 +0100 Subject: mfd: twl4030-madc: Cleanup driver Some style fixes in twl4030-madc driver. Reported-by: Jonathan Cameron Reported-by: Lee Jones Signed-off-by: Sebastian Reichel Acked-by: Jonathan Cameron Tested-by: Marek Belisko Signed-off-by: Lee Jones --- drivers/mfd/twl4030-madc.c | 145 ++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 93ef91229cd3..98b9c9874d26 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -49,22 +49,22 @@ #include -/* +/** * struct twl4030_madc_data - a container for madc info - * @dev - pointer to device structure for madc - * @lock - mutex protecting this data structure - * @requests - Array of request struct corresponding to SW1, SW2 and RT - * @use_second_irq - IRQ selection (main or co-processor) - * @imr - Interrupt mask register of MADC - * @isr - Interrupt status register of MADC + * @dev: Pointer to device structure for madc + * @lock: Mutex protecting this data structure + * @requests: Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq: IRQ selection (main or co-processor) + * @imr: Interrupt mask register of MADC + * @isr: Interrupt status register of MADC */ struct twl4030_madc_data { struct device *dev; struct mutex lock; /* mutex protecting this data structure */ struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; bool use_second_irq; - int imr; - int isr; + u8 imr; + u8 isr; }; static int twl4030_madc_read(struct iio_dev *iio_dev, @@ -155,17 +155,16 @@ twl4030_divider_ratios[16] = { }; -/* - * Conversion table from -3 to 55 degree Celcius - */ -static int therm_tbl[] = { -30800, 29500, 28300, 27100, -26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, -17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, -11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, -8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, -5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, -4040, 3910, 3790, 3670, 3550 +/* Conversion table from -3 to 55 degrees Celcius */ +static int twl4030_therm_tbl[] = { + 30800, 29500, 28300, 27100, + 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, + 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, + 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, + 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, + 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, + 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, + 3550 }; /* @@ -197,11 +196,12 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { }, }; -/* - * Function to read a particular channel value. - * @madc - pointer to struct twl4030_madc_data - * @reg - lsb of ADC Channel - * If the i2c read fails it returns an error else returns 0. +/** + * twl4030_madc_channel_raw_read() - Function to read a particular channel value + * @madc: pointer to struct twl4030_madc_data + * @reg: lsb of ADC Channel + * + * Return: 0 on success, an error code otherwise. */ static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) { @@ -227,7 +227,7 @@ static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) } /* - * Return battery temperature + * Return battery temperature in degrees Celsius * Or < 0 on failure. */ static int twl4030battery_temperature(int raw_volt) @@ -236,18 +236,18 @@ static int twl4030battery_temperature(int raw_volt) int temp, curr, volt, res, ret; volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; - /* Getting and calculating the supply current in micro ampers */ + /* Getting and calculating the supply current in micro amperes */ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, REG_BCICTL2); if (ret < 0) return ret; + curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; /* Getting and calculating the thermistor resistance in ohms */ res = volt * 1000 / curr; /* calculating temperature */ for (temp = 58; temp >= 0; temp--) { - int actual = therm_tbl[temp]; - + int actual = twl4030_therm_tbl[temp]; if ((actual - res) >= 0) break; } @@ -269,11 +269,12 @@ static int twl4030battery_current(int raw_volt) else /* slope of 0.88 mV/mA */ return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; } + /* * Function to read channel values * @madc - pointer to twl4030_madc_data struct * @reg_base - Base address of the first channel - * @Channels - 16 bit bitmap. If the bit is set, channel value is read + * @Channels - 16 bit bitmap. If the bit is set, channel's value is read * @buf - The channel values are stored here. if read fails error * @raw - Return raw values without conversion * value is stored @@ -284,17 +285,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, long channels, int *buf, bool raw) { - int count = 0, count_req = 0, i; + int count = 0; + int i; u8 reg; for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { - reg = reg_base + 2 * i; + reg = reg_base + (2 * i); buf[i] = twl4030_madc_channel_raw_read(madc, reg); if (buf[i] < 0) { - dev_err(madc->dev, - "Unable to read register 0x%X\n", reg); - count_req++; - continue; + dev_err(madc->dev, "Unable to read register 0x%X\n", + reg); + return buf[i]; } if (raw) { count++; @@ -305,7 +306,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_current(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading current\n"); - count_req++; + return buf[i]; } else { count++; buf[i] = buf[i] - 750; @@ -315,7 +316,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_temperature(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading temperature\n"); - count_req++; + return buf[i]; } else { buf[i] -= 3; count++; @@ -336,8 +337,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, twl4030_divider_ratios[i].numerator); } } - if (count_req) - dev_err(madc->dev, "%d channel conversion failed\n", count_req); return count; } @@ -361,13 +360,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) madc->imr); return ret; } + val &= ~(1 << id); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); if (ret) { dev_err(madc->dev, "unable to write imr register 0x%X\n", madc->imr); return ret; - } return 0; @@ -430,7 +429,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) continue; ret = twl4030_madc_disable_irq(madc, i); if (ret < 0) - dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); + dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); madc->requests[i].result_pending = 1; } for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { @@ -512,21 +511,17 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, { const struct twl4030_madc_conversion_method *method; int ret = 0; + + if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) + return -ENOTSUPP; + method = &twl4030_conversion_methods[conv_method]; - switch (conv_method) { - case TWL4030_MADC_SW1: - case TWL4030_MADC_SW2: - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, - TWL4030_MADC_SW_START, method->ctrl); - if (ret) { - dev_err(madc->dev, - "unable to write ctrl register 0x%X\n", - method->ctrl); - return ret; - } - break; - default: - break; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, + method->ctrl); + if (ret) { + dev_err(madc->dev, "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; } return 0; @@ -623,8 +618,8 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) ch_lsb, method->avg); if (ret) { dev_err(twl4030_madc->dev, - "unable to write sel reg 0x%X\n", - method->sel + 1); + "unable to write avg reg 0x%X\n", + method->avg); goto out; } } @@ -665,10 +660,6 @@ out: } EXPORT_SYMBOL_GPL(twl4030_madc_conversion); -/* - * Return channel value - * Or < 0 on failure. - */ int twl4030_get_madc_conversion(int channel_no) { struct twl4030_madc_request req; @@ -689,20 +680,25 @@ int twl4030_get_madc_conversion(int channel_no) } EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); -/* +/** + * twl4030_madc_set_current_generator() - setup bias current + * + * @madc: pointer to twl4030_madc_data struct + * @chan: can be one of the two values: + * TWL4030_BCI_ITHEN + * Enables bias current for main battery type reading + * TWL4030_BCI_TYPEN + * Enables bias current for main battery temperature sensing + * @on: enable or disable chan. + * * Function to enable or disable bias current for * main battery type reading or temperature sensing - * @madc - pointer to twl4030_madc_data struct - * @chan - can be one of the two values - * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading - * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature - * sensing - * @on - enable or disable chan. */ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, int chan, int on) { int ret; + int regmask; u8 regval; ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, @@ -712,10 +708,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, TWL4030_BCI_BCICTL1); return ret; } + + regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; if (on) - regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + regval |= regmask; else - regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; + regval &= ~regmask; + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { @@ -730,7 +729,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, /* * Function that sets MADC software power on bit to enable MADC * @madc - pointer to twl4030_madc_data struct - * @on - Enable or disable MADC software powen on bit. + * @on - Enable or disable MADC software power on bit. * returns error if i2c read/write fails else 0 */ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) @@ -909,7 +908,7 @@ static struct platform_driver twl4030_madc_driver = { .name = "twl4030_madc", .owner = THIS_MODULE, .of_match_table = of_match_ptr(twl_madc_of_match), - }, + }, }; module_platform_driver(twl4030_madc_driver); -- cgit v1.2.3 From 168ae30802368deadccaa8a8e85057efbae22975 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 16 Mar 2014 02:43:29 +0100 Subject: mfd: twl4030-madc: Use twl_i2c_read/write_u16 for 16 bit registers Simplify reading and writing of 16 bit TWL registers in the driver by using twl_i2c_read_u16 and twl_i2c_write_u16. Signed-off-by: Sebastian Reichel Acked-by: Jonathan Cameron Tested-by: Marek Belisko Signed-off-by: Lee Jones --- drivers/mfd/twl4030-madc.c | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 98b9c9874d26..8a014fbb98bc 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -205,25 +205,19 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { */ static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) { - u8 msb, lsb; + u16 val; int ret; /* * For each ADC channel, we have MSB and LSB register pair. MSB address * is always LSB address+1. reg parameter is the address of LSB register */ - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); + ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); if (ret) { - dev_err(madc->dev, "unable to read MSB register 0x%X\n", - reg + 1); - return ret; - } - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg); - if (ret) { - dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); + dev_err(madc->dev, "unable to read register 0x%X\n", reg); return ret; } - return (int)(((msb << 8) | lsb) >> 6); + return (int)(val >> 6); } /* @@ -572,7 +566,6 @@ static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, int twl4030_madc_conversion(struct twl4030_madc_request *req) { const struct twl4030_madc_conversion_method *method; - u8 ch_msb, ch_lsb; int ret; if (!req || !twl4030_madc) @@ -588,37 +581,21 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) ret = -EBUSY; goto out; } - ch_msb = (req->channels >> 8) & 0xff; - ch_lsb = req->channels & 0xff; method = &twl4030_conversion_methods[req->method]; /* Select channels to be converted */ - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); if (ret) { dev_err(twl4030_madc->dev, - "unable to write sel register 0x%X\n", method->sel + 1); - goto out; - } - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); - if (ret) { - dev_err(twl4030_madc->dev, - "unable to write sel register 0x%X\n", method->sel + 1); + "unable to write sel register 0x%X\n", method->sel); goto out; } /* Select averaging for all channels if do_avg is set */ if (req->do_avg) { - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, - ch_msb, method->avg + 1); + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, + method->avg); if (ret) { dev_err(twl4030_madc->dev, "unable to write avg register 0x%X\n", - method->avg + 1); - goto out; - } - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, - ch_lsb, method->avg); - if (ret) { - dev_err(twl4030_madc->dev, - "unable to write avg reg 0x%X\n", method->avg); goto out; } -- cgit v1.2.3 From b2931b98cebfb30d940f248d2054b5268eae87a4 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 16 Mar 2014 02:43:31 +0100 Subject: mfd: twl4030-madc: Move driver to drivers/iio/adc This is a driver for an A/D converter, which belongs into drivers/iio/adc. Signed-off-by: Sebastian Reichel Acked-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/twl4030-madc.c | 896 +++++++++++++++++++++++++++++++++++++++++ drivers/mfd/Kconfig | 10 - drivers/mfd/Makefile | 1 - drivers/mfd/twl4030-madc.c | 896 ----------------------------------------- 6 files changed, 907 insertions(+), 907 deletions(-) create mode 100644 drivers/iio/adc/twl4030-madc.c delete mode 100644 drivers/mfd/twl4030-madc.c (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2209f28441e9..427f75c4f69e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -183,6 +183,16 @@ config TI_AM335X_ADC Say yes here to build support for Texas Instruments ADC driver which is also a MFD client. +config TWL4030_MADC + tristate "TWL4030 MADC (Monitoring A/D Converter)" + depends on TWL4030_CORE + help + This driver provides support for Triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can also be built as a module. If so, the module will be + called twl4030-madc. + config TWL6030_GPADC tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" depends on TWL4030_CORE diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ba9a10a24cd0..9acf2df2c1a8 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c new file mode 100644 index 000000000000..8a014fbb98bc --- /dev/null +++ b/drivers/iio/adc/twl4030-madc.c @@ -0,0 +1,896 @@ +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen + * + * Amit Kucheria + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * struct twl4030_madc_data - a container for madc info + * @dev: Pointer to device structure for madc + * @lock: Mutex protecting this data structure + * @requests: Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq: IRQ selection (main or co-processor) + * @imr: Interrupt mask register of MADC + * @isr: Interrupt status register of MADC + */ +struct twl4030_madc_data { + struct device *dev; + struct mutex lock; /* mutex protecting this data structure */ + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + bool use_second_irq; + u8 imr; + u8 isr; +}; + +static int twl4030_madc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl4030_madc_data *madc = iio_priv(iio_dev); + struct twl4030_madc_request req; + int ret; + + req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; + + req.channels = BIT(chan->channel); + req.active = false; + req.func_cb = NULL; + req.type = TWL4030_MADC_WAIT; + req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); + req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); + + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + + *val = req.rbuf[chan->channel]; + + return IIO_VAL_INT; +} + +static const struct iio_info twl4030_madc_iio_info = { + .read_raw = &twl4030_madc_read, + .driver_module = THIS_MODULE, +}; + +#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = _name, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl4030_madc_iio_channels[] = { + TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), + TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), + TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), + TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), + TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), + TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), + TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), + TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), + TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), + TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), + TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), + TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), + TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), + TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), + TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), + TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), +}; + +static struct twl4030_madc_data *twl4030_madc; + +struct twl4030_prescale_divider_ratios { + s16 numerator; + s16 denominator; +}; + +static const struct twl4030_prescale_divider_ratios +twl4030_divider_ratios[16] = { + {1, 1}, /* CHANNEL 0 No Prescaler */ + {1, 1}, /* CHANNEL 1 No Prescaler */ + {6, 10}, /* CHANNEL 2 */ + {6, 10}, /* CHANNEL 3 */ + {6, 10}, /* CHANNEL 4 */ + {6, 10}, /* CHANNEL 5 */ + {6, 10}, /* CHANNEL 6 */ + {6, 10}, /* CHANNEL 7 */ + {3, 14}, /* CHANNEL 8 */ + {1, 3}, /* CHANNEL 9 */ + {1, 1}, /* CHANNEL 10 No Prescaler */ + {15, 100}, /* CHANNEL 11 */ + {1, 4}, /* CHANNEL 12 */ + {1, 1}, /* CHANNEL 13 Reserved channels */ + {1, 1}, /* CHANNEL 14 Reseved channels */ + {5, 11}, /* CHANNEL 15 */ +}; + + +/* Conversion table from -3 to 55 degrees Celcius */ +static int twl4030_therm_tbl[] = { + 30800, 29500, 28300, 27100, + 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, + 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, + 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, + 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, + 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, + 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, + 3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { + [TWL4030_MADC_RT] = { + .sel = TWL4030_MADC_RTSELECT_LSB, + .avg = TWL4030_MADC_RTAVERAGE_LSB, + .rbase = TWL4030_MADC_RTCH0_LSB, + }, + [TWL4030_MADC_SW1] = { + .sel = TWL4030_MADC_SW1SELECT_LSB, + .avg = TWL4030_MADC_SW1AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW1, + }, + [TWL4030_MADC_SW2] = { + .sel = TWL4030_MADC_SW2SELECT_LSB, + .avg = TWL4030_MADC_SW2AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW2, + }, +}; + +/** + * twl4030_madc_channel_raw_read() - Function to read a particular channel value + * @madc: pointer to struct twl4030_madc_data + * @reg: lsb of ADC Channel + * + * Return: 0 on success, an error code otherwise. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ + u16 val; + int ret; + /* + * For each ADC channel, we have MSB and LSB register pair. MSB address + * is always LSB address+1. reg parameter is the address of LSB register + */ + ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); + if (ret) { + dev_err(madc->dev, "unable to read register 0x%X\n", reg); + return ret; + } + + return (int)(val >> 6); +} + +/* + * Return battery temperature in degrees Celsius + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ + u8 val; + int temp, curr, volt, res, ret; + + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; + /* Getting and calculating the supply current in micro amperes */ + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + REG_BCICTL2); + if (ret < 0) + return ret; + + curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; + /* Getting and calculating the thermistor resistance in ohms */ + res = volt * 1000 / curr; + /* calculating temperature */ + for (temp = 58; temp >= 0; temp--) { + int actual = twl4030_therm_tbl[temp]; + if ((actual - res) >= 0) + break; + } + + return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ + int ret; + u8 val; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + TWL4030_BCI_BCICTL1); + if (ret) + return ret; + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; + else /* slope of 0.88 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} + +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel's value is read + * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, + u8 reg_base, unsigned + long channels, int *buf, + bool raw) +{ + int count = 0; + int i; + u8 reg; + + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { + reg = reg_base + (2 * i); + buf[i] = twl4030_madc_channel_raw_read(madc, reg); + if (buf[i] < 0) { + dev_err(madc->dev, "Unable to read register 0x%X\n", + reg); + return buf[i]; + } + if (raw) { + count++; + continue; + } + switch (i) { + case 10: + buf[i] = twl4030battery_current(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading current\n"); + return buf[i]; + } else { + count++; + buf[i] = buf[i] - 750; + } + break; + case 1: + buf[i] = twl4030battery_temperature(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading temperature\n"); + return buf[i]; + } else { + buf[i] -= 3; + count++; + } + break; + default: + count++; + /* Analog Input (V) = conv_result * step_size / R + * conv_result = decimal value of 10-bit conversion + * result + * step size = 1.5 / (2 ^ 10 -1) + * R = Prescaler ratio for input channels. + * Result given in mV hence multiplied by 1000. + */ + buf[i] = (buf[i] * 3 * 1000 * + twl4030_divider_ratios[i].denominator) + / (2 * 1023 * + twl4030_divider_ratios[i].numerator); + } + } + + return count; +} + +/* + * Enables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be enabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + + val &= ~(1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val |= (1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ + struct twl4030_madc_data *madc = _madc; + const struct twl4030_madc_conversion_method *method; + u8 isr_val, imr_val; + int i, len, ret; + struct twl4030_madc_request *r; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); + if (ret) { + dev_err(madc->dev, "unable to read isr register 0x%X\n", + madc->isr); + goto err_i2c; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + goto err_i2c; + } + isr_val &= ~imr_val; + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + if (!(isr_val & (1 << i))) + continue; + ret = twl4030_madc_disable_irq(madc, i); + if (ret < 0) + dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); + madc->requests[i].result_pending = 1; + } + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + /* No pending results for this method, move to next one */ + if (!r->result_pending) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; + +err_i2c: + /* + * In case of error check whichever request is active + * and service the same. + */ + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + if (r->active == 0) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; +} + +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, + struct twl4030_madc_request *req) +{ + struct twl4030_madc_request *p; + int ret; + + p = &madc->requests[req->method]; + memcpy(p, req, sizeof(*req)); + ret = twl4030_madc_enable_irq(madc, req->method); + if (ret < 0) { + dev_err(madc->dev, "enable irq failed!!\n"); + return ret; + } + + return 0; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, + int conv_method) +{ + const struct twl4030_madc_conversion_method *method; + int ret = 0; + + if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) + return -ENOTSUPP; + + method = &twl4030_conversion_methods[conv_method]; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, + method->ctrl); + if (ret) { + dev_err(madc->dev, "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; + } + + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ + const struct twl4030_madc_conversion_method *method; + int ret; + + if (!req || !twl4030_madc) + return -EINVAL; + + mutex_lock(&twl4030_madc->lock); + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { + ret = -EINVAL; + goto out; + } + /* Do we have a conversion request ongoing */ + if (twl4030_madc->requests[req->method].active) { + ret = -EBUSY; + goto out; + } + method = &twl4030_conversion_methods[req->method]; + /* Select channels to be converted */ + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel); + goto out; + } + /* Select averaging for all channels if do_avg is set */ + if (req->do_avg) { + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, + method->avg); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write avg register 0x%X\n", + method->avg); + goto out; + } + } + if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { + ret = twl4030_madc_set_irq(twl4030_madc, req); + if (ret < 0) + goto out; + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + ret = 0; + goto out; + } + /* With RT method we should not be here anymore */ + if (req->method == TWL4030_MADC_RT) { + ret = -EINVAL; + goto out; + } + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + /* Wait until conversion is ready (ctrl register returns EOC) */ + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); + if (ret) { + twl4030_madc->requests[req->method].active = 0; + goto out; + } + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, + req->channels, req->rbuf, req->raw); + twl4030_madc->requests[req->method].active = 0; + +out: + mutex_unlock(&twl4030_madc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(twl4030_madc_conversion); + +int twl4030_get_madc_conversion(int channel_no) +{ + struct twl4030_madc_request req; + int temp = 0; + int ret; + + req.channels = (1 << channel_no); + req.method = TWL4030_MADC_SW2; + req.active = 0; + req.func_cb = NULL; + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + if (req.rbuf[channel_no] > 0) + temp = req.rbuf[channel_no]; + + return temp; +} +EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); + +/** + * twl4030_madc_set_current_generator() - setup bias current + * + * @madc: pointer to twl4030_madc_data struct + * @chan: can be one of the two values: + * TWL4030_BCI_ITHEN + * Enables bias current for main battery type reading + * TWL4030_BCI_TYPEN + * Enables bias current for main battery temperature sensing + * @on: enable or disable chan. + * + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, + int chan, int on) +{ + int ret; + int regmask; + u8 regval; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", + TWL4030_BCI_BCICTL1); + return ret; + } + + regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + if (on) + regval |= regmask; + else + regval &= ~regmask; + + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", + TWL4030_BCI_BCICTL1); + return ret; + } + + return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software power on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ + u8 regval; + int ret; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + if (on) + regval |= TWL4030_MADC_MADCON; + else + regval &= ~TWL4030_MADC_MADCON; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + + return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int twl4030_madc_probe(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc; + struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int irq, ret; + u8 regval; + struct iio_dev *iio_dev = NULL; + + if (!pdata && !np) { + dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); + return -EINVAL; + } + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); + if (!iio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + madc = iio_priv(iio_dev); + madc->dev = &pdev->dev; + + iio_dev->name = dev_name(&pdev->dev); + iio_dev->dev.parent = &pdev->dev; + iio_dev->dev.of_node = pdev->dev.of_node; + iio_dev->info = &twl4030_madc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = twl4030_madc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); + + /* + * Phoenix provides 2 interrupt lines. The first one is connected to + * the OMAP. The other one can be connected to the other processor such + * as modem. Hence two separate ISR and IMR registers. + */ + if (pdata) + madc->use_second_irq = (pdata->irq_line != 1); + else + madc->use_second_irq = of_property_read_bool(np, + "ti,system-uses-second-madc-irq"); + + madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : + TWL4030_MADC_IMR1; + madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : + TWL4030_MADC_ISR1; + + ret = twl4030_madc_set_power(madc, 1); + if (ret < 0) + return ret; + ret = twl4030_madc_set_current_generator(madc, 0, 1); + if (ret < 0) + goto err_current_generator; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + regval |= TWL4030_BCI_MESBAT; + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + + /* Check that MADC clock is on */ + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + + /* If MADC clk is not on, turn it on */ + if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { + dev_info(&pdev->dev, "clk disabled, enabling\n"); + regval |= TWL4030_GPBR1_MADC_HFCLK_EN; + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, + TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + } + + platform_set_drvdata(pdev, iio_dev); + mutex_init(&madc->lock); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + twl4030_madc_threaded_irq_handler, + IRQF_TRIGGER_RISING, "twl4030_madc", madc); + if (ret) { + dev_err(&pdev->dev, "could not request irq\n"); + goto err_i2c; + } + twl4030_madc = madc; + + ret = iio_device_register(iio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio device\n"); + goto err_i2c; + } + + return 0; + +err_i2c: + twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: + twl4030_madc_set_power(madc, 0); + return ret; +} + +static int twl4030_madc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio_dev = platform_get_drvdata(pdev); + struct twl4030_madc_data *madc = iio_priv(iio_dev); + + iio_device_unregister(iio_dev); + + twl4030_madc_set_current_generator(madc, 0, 0); + twl4030_madc_set_power(madc, 0); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl_madc_of_match[] = { + { .compatible = "ti,twl4030-madc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_madc_of_match); +#endif + +static struct platform_driver twl4030_madc_driver = { + .probe = twl4030_madc_probe, + .remove = twl4030_madc_remove, + .driver = { + .name = "twl4030_madc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl_madc_of_match), + }, +}; + +module_platform_driver(twl4030_madc_driver); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 49bb445d846a..23a8a51f6860 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -935,16 +935,6 @@ config TWL4030_CORE high speed USB OTG transceiver, an audio codec (on most versions) and many other features. -config TWL4030_MADC - tristate "TI TWL4030 MADC" - depends on TWL4030_CORE - help - This driver provides support for triton TWL4030-MADC. The - driver supports both RT and SW conversion methods. - - This driver can be built as a module. If so it will be - named twl4030-madc - config TWL4030_POWER bool "TI TWL4030 power resources" depends on TWL4030_CORE && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5aea5ef0a62f..c8eb0bcf4da0 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -71,7 +71,6 @@ obj-$(CONFIG_MFD_TPS80031) += tps80031.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o -obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6040_CORE) += twl6040.o diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c deleted file mode 100644 index 8a014fbb98bc..000000000000 --- a/drivers/mfd/twl4030-madc.c +++ /dev/null @@ -1,896 +0,0 @@ -/* - * - * TWL4030 MADC module driver-This driver monitors the real time - * conversion of analog signals like battery temperature, - * battery type, battery level etc. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * J Keerthy - * - * Based on twl4030-madc.c - * Copyright (C) 2008 Nokia Corporation - * Mikko Ylinen - * - * Amit Kucheria - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - * struct twl4030_madc_data - a container for madc info - * @dev: Pointer to device structure for madc - * @lock: Mutex protecting this data structure - * @requests: Array of request struct corresponding to SW1, SW2 and RT - * @use_second_irq: IRQ selection (main or co-processor) - * @imr: Interrupt mask register of MADC - * @isr: Interrupt status register of MADC - */ -struct twl4030_madc_data { - struct device *dev; - struct mutex lock; /* mutex protecting this data structure */ - struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; - bool use_second_irq; - u8 imr; - u8 isr; -}; - -static int twl4030_madc_read(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - int *val, int *val2, long mask) -{ - struct twl4030_madc_data *madc = iio_priv(iio_dev); - struct twl4030_madc_request req; - int ret; - - req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; - - req.channels = BIT(chan->channel); - req.active = false; - req.func_cb = NULL; - req.type = TWL4030_MADC_WAIT; - req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); - req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); - - ret = twl4030_madc_conversion(&req); - if (ret < 0) - return ret; - - *val = req.rbuf[chan->channel]; - - return IIO_VAL_INT; -} - -static const struct iio_info twl4030_madc_iio_info = { - .read_raw = &twl4030_madc_read, - .driver_module = THIS_MODULE, -}; - -#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ - .type = _type, \ - .channel = _channel, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ - BIT(IIO_CHAN_INFO_PROCESSED), \ - .datasheet_name = _name, \ - .indexed = 1, \ -} - -static const struct iio_chan_spec twl4030_madc_iio_channels[] = { - TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), - TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), - TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), - TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), - TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), - TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), - TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), - TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), - TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), - TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), - TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), - TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), - TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), - TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), - TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), - TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), -}; - -static struct twl4030_madc_data *twl4030_madc; - -struct twl4030_prescale_divider_ratios { - s16 numerator; - s16 denominator; -}; - -static const struct twl4030_prescale_divider_ratios -twl4030_divider_ratios[16] = { - {1, 1}, /* CHANNEL 0 No Prescaler */ - {1, 1}, /* CHANNEL 1 No Prescaler */ - {6, 10}, /* CHANNEL 2 */ - {6, 10}, /* CHANNEL 3 */ - {6, 10}, /* CHANNEL 4 */ - {6, 10}, /* CHANNEL 5 */ - {6, 10}, /* CHANNEL 6 */ - {6, 10}, /* CHANNEL 7 */ - {3, 14}, /* CHANNEL 8 */ - {1, 3}, /* CHANNEL 9 */ - {1, 1}, /* CHANNEL 10 No Prescaler */ - {15, 100}, /* CHANNEL 11 */ - {1, 4}, /* CHANNEL 12 */ - {1, 1}, /* CHANNEL 13 Reserved channels */ - {1, 1}, /* CHANNEL 14 Reseved channels */ - {5, 11}, /* CHANNEL 15 */ -}; - - -/* Conversion table from -3 to 55 degrees Celcius */ -static int twl4030_therm_tbl[] = { - 30800, 29500, 28300, 27100, - 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, - 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, - 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, - 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, - 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, - 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, - 3550 -}; - -/* - * Structure containing the registers - * of different conversion methods supported by MADC. - * Hardware or RT real time conversion request initiated by external host - * processor for RT Signal conversions. - * External host processors can also request for non RT conversions - * SW1 and SW2 software conversions also called asynchronous or GPC request. - */ -static -const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { - [TWL4030_MADC_RT] = { - .sel = TWL4030_MADC_RTSELECT_LSB, - .avg = TWL4030_MADC_RTAVERAGE_LSB, - .rbase = TWL4030_MADC_RTCH0_LSB, - }, - [TWL4030_MADC_SW1] = { - .sel = TWL4030_MADC_SW1SELECT_LSB, - .avg = TWL4030_MADC_SW1AVERAGE_LSB, - .rbase = TWL4030_MADC_GPCH0_LSB, - .ctrl = TWL4030_MADC_CTRL_SW1, - }, - [TWL4030_MADC_SW2] = { - .sel = TWL4030_MADC_SW2SELECT_LSB, - .avg = TWL4030_MADC_SW2AVERAGE_LSB, - .rbase = TWL4030_MADC_GPCH0_LSB, - .ctrl = TWL4030_MADC_CTRL_SW2, - }, -}; - -/** - * twl4030_madc_channel_raw_read() - Function to read a particular channel value - * @madc: pointer to struct twl4030_madc_data - * @reg: lsb of ADC Channel - * - * Return: 0 on success, an error code otherwise. - */ -static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) -{ - u16 val; - int ret; - /* - * For each ADC channel, we have MSB and LSB register pair. MSB address - * is always LSB address+1. reg parameter is the address of LSB register - */ - ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); - if (ret) { - dev_err(madc->dev, "unable to read register 0x%X\n", reg); - return ret; - } - - return (int)(val >> 6); -} - -/* - * Return battery temperature in degrees Celsius - * Or < 0 on failure. - */ -static int twl4030battery_temperature(int raw_volt) -{ - u8 val; - int temp, curr, volt, res, ret; - - volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; - /* Getting and calculating the supply current in micro amperes */ - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, - REG_BCICTL2); - if (ret < 0) - return ret; - - curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; - /* Getting and calculating the thermistor resistance in ohms */ - res = volt * 1000 / curr; - /* calculating temperature */ - for (temp = 58; temp >= 0; temp--) { - int actual = twl4030_therm_tbl[temp]; - if ((actual - res) >= 0) - break; - } - - return temp + 1; -} - -static int twl4030battery_current(int raw_volt) -{ - int ret; - u8 val; - - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, - TWL4030_BCI_BCICTL1); - if (ret) - return ret; - if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ - return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; - else /* slope of 0.88 mV/mA */ - return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; -} - -/* - * Function to read channel values - * @madc - pointer to twl4030_madc_data struct - * @reg_base - Base address of the first channel - * @Channels - 16 bit bitmap. If the bit is set, channel's value is read - * @buf - The channel values are stored here. if read fails error - * @raw - Return raw values without conversion - * value is stored - * Returns the number of successfully read channels. - */ -static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, - u8 reg_base, unsigned - long channels, int *buf, - bool raw) -{ - int count = 0; - int i; - u8 reg; - - for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { - reg = reg_base + (2 * i); - buf[i] = twl4030_madc_channel_raw_read(madc, reg); - if (buf[i] < 0) { - dev_err(madc->dev, "Unable to read register 0x%X\n", - reg); - return buf[i]; - } - if (raw) { - count++; - continue; - } - switch (i) { - case 10: - buf[i] = twl4030battery_current(buf[i]); - if (buf[i] < 0) { - dev_err(madc->dev, "err reading current\n"); - return buf[i]; - } else { - count++; - buf[i] = buf[i] - 750; - } - break; - case 1: - buf[i] = twl4030battery_temperature(buf[i]); - if (buf[i] < 0) { - dev_err(madc->dev, "err reading temperature\n"); - return buf[i]; - } else { - buf[i] -= 3; - count++; - } - break; - default: - count++; - /* Analog Input (V) = conv_result * step_size / R - * conv_result = decimal value of 10-bit conversion - * result - * step size = 1.5 / (2 ^ 10 -1) - * R = Prescaler ratio for input channels. - * Result given in mV hence multiplied by 1000. - */ - buf[i] = (buf[i] * 3 * 1000 * - twl4030_divider_ratios[i].denominator) - / (2 * 1023 * - twl4030_divider_ratios[i].numerator); - } - } - - return count; -} - -/* - * Enables irq. - * @madc - pointer to twl4030_madc_data struct - * @id - irq number to be enabled - * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 - * corresponding to RT, SW1, SW2 conversion requests. - * If the i2c read fails it returns an error else returns 0. - */ -static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) -{ - u8 val; - int ret; - - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); - if (ret) { - dev_err(madc->dev, "unable to read imr register 0x%X\n", - madc->imr); - return ret; - } - - val &= ~(1 << id); - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); - if (ret) { - dev_err(madc->dev, - "unable to write imr register 0x%X\n", madc->imr); - return ret; - } - - return 0; -} - -/* - * Disables irq. - * @madc - pointer to twl4030_madc_data struct - * @id - irq number to be disabled - * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 - * corresponding to RT, SW1, SW2 conversion requests. - * Returns error if i2c read/write fails. - */ -static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) -{ - u8 val; - int ret; - - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); - if (ret) { - dev_err(madc->dev, "unable to read imr register 0x%X\n", - madc->imr); - return ret; - } - val |= (1 << id); - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); - if (ret) { - dev_err(madc->dev, - "unable to write imr register 0x%X\n", madc->imr); - return ret; - } - - return 0; -} - -static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) -{ - struct twl4030_madc_data *madc = _madc; - const struct twl4030_madc_conversion_method *method; - u8 isr_val, imr_val; - int i, len, ret; - struct twl4030_madc_request *r; - - mutex_lock(&madc->lock); - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); - if (ret) { - dev_err(madc->dev, "unable to read isr register 0x%X\n", - madc->isr); - goto err_i2c; - } - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); - if (ret) { - dev_err(madc->dev, "unable to read imr register 0x%X\n", - madc->imr); - goto err_i2c; - } - isr_val &= ~imr_val; - for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { - if (!(isr_val & (1 << i))) - continue; - ret = twl4030_madc_disable_irq(madc, i); - if (ret < 0) - dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); - madc->requests[i].result_pending = 1; - } - for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { - r = &madc->requests[i]; - /* No pending results for this method, move to next one */ - if (!r->result_pending) - continue; - method = &twl4030_conversion_methods[r->method]; - /* Read results */ - len = twl4030_madc_read_channels(madc, method->rbase, - r->channels, r->rbuf, r->raw); - /* Return results to caller */ - if (r->func_cb != NULL) { - r->func_cb(len, r->channels, r->rbuf); - r->func_cb = NULL; - } - /* Free request */ - r->result_pending = 0; - r->active = 0; - } - mutex_unlock(&madc->lock); - - return IRQ_HANDLED; - -err_i2c: - /* - * In case of error check whichever request is active - * and service the same. - */ - for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { - r = &madc->requests[i]; - if (r->active == 0) - continue; - method = &twl4030_conversion_methods[r->method]; - /* Read results */ - len = twl4030_madc_read_channels(madc, method->rbase, - r->channels, r->rbuf, r->raw); - /* Return results to caller */ - if (r->func_cb != NULL) { - r->func_cb(len, r->channels, r->rbuf); - r->func_cb = NULL; - } - /* Free request */ - r->result_pending = 0; - r->active = 0; - } - mutex_unlock(&madc->lock); - - return IRQ_HANDLED; -} - -static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, - struct twl4030_madc_request *req) -{ - struct twl4030_madc_request *p; - int ret; - - p = &madc->requests[req->method]; - memcpy(p, req, sizeof(*req)); - ret = twl4030_madc_enable_irq(madc, req->method); - if (ret < 0) { - dev_err(madc->dev, "enable irq failed!!\n"); - return ret; - } - - return 0; -} - -/* - * Function which enables the madc conversion - * by writing to the control register. - * @madc - pointer to twl4030_madc_data struct - * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 - * corresponding to RT SW1 or SW2 conversion methods. - * Returns 0 if succeeds else a negative error value - */ -static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, - int conv_method) -{ - const struct twl4030_madc_conversion_method *method; - int ret = 0; - - if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) - return -ENOTSUPP; - - method = &twl4030_conversion_methods[conv_method]; - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, - method->ctrl); - if (ret) { - dev_err(madc->dev, "unable to write ctrl register 0x%X\n", - method->ctrl); - return ret; - } - - return 0; -} - -/* - * Function that waits for conversion to be ready - * @madc - pointer to twl4030_madc_data struct - * @timeout_ms - timeout value in milliseconds - * @status_reg - ctrl register - * returns 0 if succeeds else a negative error value - */ -static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, - unsigned int timeout_ms, - u8 status_reg) -{ - unsigned long timeout; - int ret; - - timeout = jiffies + msecs_to_jiffies(timeout_ms); - do { - u8 reg; - - ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); - if (ret) { - dev_err(madc->dev, - "unable to read status register 0x%X\n", - status_reg); - return ret; - } - if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) - return 0; - usleep_range(500, 2000); - } while (!time_after(jiffies, timeout)); - dev_err(madc->dev, "conversion timeout!\n"); - - return -EAGAIN; -} - -/* - * An exported function which can be called from other kernel drivers. - * @req twl4030_madc_request structure - * req->rbuf will be filled with read values of channels based on the - * channel index. If a particular channel reading fails there will - * be a negative error value in the corresponding array element. - * returns 0 if succeeds else error value - */ -int twl4030_madc_conversion(struct twl4030_madc_request *req) -{ - const struct twl4030_madc_conversion_method *method; - int ret; - - if (!req || !twl4030_madc) - return -EINVAL; - - mutex_lock(&twl4030_madc->lock); - if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { - ret = -EINVAL; - goto out; - } - /* Do we have a conversion request ongoing */ - if (twl4030_madc->requests[req->method].active) { - ret = -EBUSY; - goto out; - } - method = &twl4030_conversion_methods[req->method]; - /* Select channels to be converted */ - ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); - if (ret) { - dev_err(twl4030_madc->dev, - "unable to write sel register 0x%X\n", method->sel); - goto out; - } - /* Select averaging for all channels if do_avg is set */ - if (req->do_avg) { - ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, - method->avg); - if (ret) { - dev_err(twl4030_madc->dev, - "unable to write avg register 0x%X\n", - method->avg); - goto out; - } - } - if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { - ret = twl4030_madc_set_irq(twl4030_madc, req); - if (ret < 0) - goto out; - ret = twl4030_madc_start_conversion(twl4030_madc, req->method); - if (ret < 0) - goto out; - twl4030_madc->requests[req->method].active = 1; - ret = 0; - goto out; - } - /* With RT method we should not be here anymore */ - if (req->method == TWL4030_MADC_RT) { - ret = -EINVAL; - goto out; - } - ret = twl4030_madc_start_conversion(twl4030_madc, req->method); - if (ret < 0) - goto out; - twl4030_madc->requests[req->method].active = 1; - /* Wait until conversion is ready (ctrl register returns EOC) */ - ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); - if (ret) { - twl4030_madc->requests[req->method].active = 0; - goto out; - } - ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, - req->channels, req->rbuf, req->raw); - twl4030_madc->requests[req->method].active = 0; - -out: - mutex_unlock(&twl4030_madc->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(twl4030_madc_conversion); - -int twl4030_get_madc_conversion(int channel_no) -{ - struct twl4030_madc_request req; - int temp = 0; - int ret; - - req.channels = (1 << channel_no); - req.method = TWL4030_MADC_SW2; - req.active = 0; - req.func_cb = NULL; - ret = twl4030_madc_conversion(&req); - if (ret < 0) - return ret; - if (req.rbuf[channel_no] > 0) - temp = req.rbuf[channel_no]; - - return temp; -} -EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); - -/** - * twl4030_madc_set_current_generator() - setup bias current - * - * @madc: pointer to twl4030_madc_data struct - * @chan: can be one of the two values: - * TWL4030_BCI_ITHEN - * Enables bias current for main battery type reading - * TWL4030_BCI_TYPEN - * Enables bias current for main battery temperature sensing - * @on: enable or disable chan. - * - * Function to enable or disable bias current for - * main battery type reading or temperature sensing - */ -static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, - int chan, int on) -{ - int ret; - int regmask; - u8 regval; - - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, - ®val, TWL4030_BCI_BCICTL1); - if (ret) { - dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", - TWL4030_BCI_BCICTL1); - return ret; - } - - regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; - if (on) - regval |= regmask; - else - regval &= ~regmask; - - ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, - regval, TWL4030_BCI_BCICTL1); - if (ret) { - dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", - TWL4030_BCI_BCICTL1); - return ret; - } - - return 0; -} - -/* - * Function that sets MADC software power on bit to enable MADC - * @madc - pointer to twl4030_madc_data struct - * @on - Enable or disable MADC software power on bit. - * returns error if i2c read/write fails else 0 - */ -static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) -{ - u8 regval; - int ret; - - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, - ®val, TWL4030_MADC_CTRL1); - if (ret) { - dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", - TWL4030_MADC_CTRL1); - return ret; - } - if (on) - regval |= TWL4030_MADC_MADCON; - else - regval &= ~TWL4030_MADC_MADCON; - ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); - if (ret) { - dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", - TWL4030_MADC_CTRL1); - return ret; - } - - return 0; -} - -/* - * Initialize MADC and request for threaded irq - */ -static int twl4030_madc_probe(struct platform_device *pdev) -{ - struct twl4030_madc_data *madc; - struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; - int irq, ret; - u8 regval; - struct iio_dev *iio_dev = NULL; - - if (!pdata && !np) { - dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); - return -EINVAL; - } - - iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); - if (!iio_dev) { - dev_err(&pdev->dev, "failed allocating iio device\n"); - return -ENOMEM; - } - - madc = iio_priv(iio_dev); - madc->dev = &pdev->dev; - - iio_dev->name = dev_name(&pdev->dev); - iio_dev->dev.parent = &pdev->dev; - iio_dev->dev.of_node = pdev->dev.of_node; - iio_dev->info = &twl4030_madc_iio_info; - iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->channels = twl4030_madc_iio_channels; - iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); - - /* - * Phoenix provides 2 interrupt lines. The first one is connected to - * the OMAP. The other one can be connected to the other processor such - * as modem. Hence two separate ISR and IMR registers. - */ - if (pdata) - madc->use_second_irq = (pdata->irq_line != 1); - else - madc->use_second_irq = of_property_read_bool(np, - "ti,system-uses-second-madc-irq"); - - madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : - TWL4030_MADC_IMR1; - madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : - TWL4030_MADC_ISR1; - - ret = twl4030_madc_set_power(madc, 1); - if (ret < 0) - return ret; - ret = twl4030_madc_set_current_generator(madc, 0, 1); - if (ret < 0) - goto err_current_generator; - - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, - ®val, TWL4030_BCI_BCICTL1); - if (ret) { - dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", - TWL4030_BCI_BCICTL1); - goto err_i2c; - } - regval |= TWL4030_BCI_MESBAT; - ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, - regval, TWL4030_BCI_BCICTL1); - if (ret) { - dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", - TWL4030_BCI_BCICTL1); - goto err_i2c; - } - - /* Check that MADC clock is on */ - ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); - if (ret) { - dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", - TWL4030_REG_GPBR1); - goto err_i2c; - } - - /* If MADC clk is not on, turn it on */ - if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { - dev_info(&pdev->dev, "clk disabled, enabling\n"); - regval |= TWL4030_GPBR1_MADC_HFCLK_EN; - ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, - TWL4030_REG_GPBR1); - if (ret) { - dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", - TWL4030_REG_GPBR1); - goto err_i2c; - } - } - - platform_set_drvdata(pdev, iio_dev); - mutex_init(&madc->lock); - - irq = platform_get_irq(pdev, 0); - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - twl4030_madc_threaded_irq_handler, - IRQF_TRIGGER_RISING, "twl4030_madc", madc); - if (ret) { - dev_err(&pdev->dev, "could not request irq\n"); - goto err_i2c; - } - twl4030_madc = madc; - - ret = iio_device_register(iio_dev); - if (ret) { - dev_err(&pdev->dev, "could not register iio device\n"); - goto err_i2c; - } - - return 0; - -err_i2c: - twl4030_madc_set_current_generator(madc, 0, 0); -err_current_generator: - twl4030_madc_set_power(madc, 0); - return ret; -} - -static int twl4030_madc_remove(struct platform_device *pdev) -{ - struct iio_dev *iio_dev = platform_get_drvdata(pdev); - struct twl4030_madc_data *madc = iio_priv(iio_dev); - - iio_device_unregister(iio_dev); - - twl4030_madc_set_current_generator(madc, 0, 0); - twl4030_madc_set_power(madc, 0); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl_madc_of_match[] = { - { .compatible = "ti,twl4030-madc", }, - { }, -}; -MODULE_DEVICE_TABLE(of, twl_madc_of_match); -#endif - -static struct platform_driver twl4030_madc_driver = { - .probe = twl4030_madc_probe, - .remove = twl4030_madc_remove, - .driver = { - .name = "twl4030_madc", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl_madc_of_match), - }, -}; - -module_platform_driver(twl4030_madc_driver); - -MODULE_DESCRIPTION("TWL4030 ADC driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("J Keerthy"); -MODULE_ALIAS("platform:twl4030_madc"); -- cgit v1.2.3 From df8eddb31f23ff54377a34d4c3034cf19b62262a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 31 Jan 2014 16:16:17 +0000 Subject: mfd: wm5102: Update register patch Update the register patch based on latest evaluation of the device. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5102-tables.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1e9a4b2102f9..f9b1e9666013 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -73,6 +73,7 @@ static const struct reg_default wm5102_revb_patch[] = { { 0x171, 0x0000 }, { 0x35E, 0x000C }, { 0x2D4, 0x0000 }, + { 0x4DC, 0x0900 }, { 0x80, 0x0000 }, }; -- cgit v1.2.3 From a6e6e660baa5c583022e3e48c85316bace027825 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Jan 2014 11:09:53 +0100 Subject: mfd: Include all drivers in subsystem menu It is currently not possible to select the SA1100 or Vexpress drivers in the MFD subsystem, because the menu for the entire subsystem ends before these options are presented. Move the main menu closing and the endif for HAS_IOMEM to the end of the file so these are selectable again. Cc: stable@vger.kernel.org Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 23a8a51f6860..30fbacfe8b3b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1183,9 +1183,6 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. -endmenu -endif - menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 @@ -1216,3 +1213,6 @@ config VEXPRESS_CONFIG help Platform configuration infrastructure for the ARM Ltd. Versatile Express. + +endmenu +endif -- cgit v1.2.3 From a0b0ea491ef59a5374640f59cdde6c53c027de53 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Jan 2014 13:18:28 +0100 Subject: mfd: max14577: Add of_compatible to extcon mfd_cell Add of_compatible ("maxim,max14577-muic") to the mfd_cell for extcon driver. If entry with such compatible is present in the DTS, the extcon driver will have of_node set. This may be useful for extcon consumers and it is documented in bindings documentation. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 71aa14a6bfbb..c9859d1baf14 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -25,7 +25,10 @@ #include static struct mfd_cell max14577_devs[] = { - { .name = "max14577-muic", }, + { + .name = "max14577-muic", + .of_compatible = "maxim,max14577-muic", + }, { .name = "max14577-regulator", .of_compatible = "maxim,max14577-regulator", -- cgit v1.2.3 From 8dbb947af37159d3df202d3cdbbae5fb896c462a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jan 2014 13:04:23 +0000 Subject: mfd: wm5102: Make additional DSP registers available to the user Expose some DSP registers which are useful for DSP users to be able to access whilst debugging their firmware. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5102-tables.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index f9b1e9666013..187ee86cbbe6 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1853,6 +1853,23 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_WDMA_BUFFER_1: + case ARIZONA_DSP1_WDMA_BUFFER_2: + case ARIZONA_DSP1_WDMA_BUFFER_3: + case ARIZONA_DSP1_WDMA_BUFFER_4: + case ARIZONA_DSP1_WDMA_BUFFER_5: + case ARIZONA_DSP1_WDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_BUFFER_7: + case ARIZONA_DSP1_WDMA_BUFFER_8: + case ARIZONA_DSP1_RDMA_BUFFER_1: + case ARIZONA_DSP1_RDMA_BUFFER_2: + case ARIZONA_DSP1_RDMA_BUFFER_3: + case ARIZONA_DSP1_RDMA_BUFFER_4: + case ARIZONA_DSP1_RDMA_BUFFER_5: + case ARIZONA_DSP1_RDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_CONFIG_1: + case ARIZONA_DSP1_WDMA_CONFIG_2: + case ARIZONA_DSP1_RDMA_CONFIG_1: case ARIZONA_DSP1_SCRATCH_0: case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: @@ -1911,6 +1928,23 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_WDMA_BUFFER_1: + case ARIZONA_DSP1_WDMA_BUFFER_2: + case ARIZONA_DSP1_WDMA_BUFFER_3: + case ARIZONA_DSP1_WDMA_BUFFER_4: + case ARIZONA_DSP1_WDMA_BUFFER_5: + case ARIZONA_DSP1_WDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_BUFFER_7: + case ARIZONA_DSP1_WDMA_BUFFER_8: + case ARIZONA_DSP1_RDMA_BUFFER_1: + case ARIZONA_DSP1_RDMA_BUFFER_2: + case ARIZONA_DSP1_RDMA_BUFFER_3: + case ARIZONA_DSP1_RDMA_BUFFER_4: + case ARIZONA_DSP1_RDMA_BUFFER_5: + case ARIZONA_DSP1_RDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_CONFIG_1: + case ARIZONA_DSP1_WDMA_CONFIG_2: + case ARIZONA_DSP1_RDMA_CONFIG_1: case ARIZONA_DSP1_SCRATCH_0: case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: -- cgit v1.2.3 From 47ec66a17cca571eb7038b7835dff7cbd5851b90 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jan 2014 13:04:24 +0000 Subject: mfd: wm5110: Make additional DSP registers available to the user Expose some DSP registers which are useful for DSP users to be able to access whilst debugging their firmware. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5110-tables.c | 168 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 11632f135e8c..ca9968525017 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -2461,6 +2461,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_STATUS_4: + case ARIZONA_DSP1_WDMA_BUFFER_1: + case ARIZONA_DSP1_WDMA_BUFFER_2: + case ARIZONA_DSP1_WDMA_BUFFER_3: + case ARIZONA_DSP1_WDMA_BUFFER_4: + case ARIZONA_DSP1_WDMA_BUFFER_5: + case ARIZONA_DSP1_WDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_BUFFER_7: + case ARIZONA_DSP1_WDMA_BUFFER_8: + case ARIZONA_DSP1_RDMA_BUFFER_1: + case ARIZONA_DSP1_RDMA_BUFFER_2: + case ARIZONA_DSP1_RDMA_BUFFER_3: + case ARIZONA_DSP1_RDMA_BUFFER_4: + case ARIZONA_DSP1_RDMA_BUFFER_5: + case ARIZONA_DSP1_RDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_CONFIG_1: + case ARIZONA_DSP1_WDMA_CONFIG_2: + case ARIZONA_DSP1_WDMA_OFFSET_1: + case ARIZONA_DSP1_RDMA_CONFIG_1: + case ARIZONA_DSP1_RDMA_OFFSET_1: + case ARIZONA_DSP1_EXTERNAL_START_SELECT_1: case ARIZONA_DSP1_SCRATCH_0: case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: @@ -2470,6 +2491,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP2_STATUS_1: case ARIZONA_DSP2_STATUS_2: case ARIZONA_DSP2_STATUS_3: + case ARIZONA_DSP2_STATUS_4: + case ARIZONA_DSP2_WDMA_BUFFER_1: + case ARIZONA_DSP2_WDMA_BUFFER_2: + case ARIZONA_DSP2_WDMA_BUFFER_3: + case ARIZONA_DSP2_WDMA_BUFFER_4: + case ARIZONA_DSP2_WDMA_BUFFER_5: + case ARIZONA_DSP2_WDMA_BUFFER_6: + case ARIZONA_DSP2_WDMA_BUFFER_7: + case ARIZONA_DSP2_WDMA_BUFFER_8: + case ARIZONA_DSP2_RDMA_BUFFER_1: + case ARIZONA_DSP2_RDMA_BUFFER_2: + case ARIZONA_DSP2_RDMA_BUFFER_3: + case ARIZONA_DSP2_RDMA_BUFFER_4: + case ARIZONA_DSP2_RDMA_BUFFER_5: + case ARIZONA_DSP2_RDMA_BUFFER_6: + case ARIZONA_DSP2_WDMA_CONFIG_1: + case ARIZONA_DSP2_WDMA_CONFIG_2: + case ARIZONA_DSP2_WDMA_OFFSET_1: + case ARIZONA_DSP2_RDMA_CONFIG_1: + case ARIZONA_DSP2_RDMA_OFFSET_1: + case ARIZONA_DSP2_EXTERNAL_START_SELECT_1: case ARIZONA_DSP2_SCRATCH_0: case ARIZONA_DSP2_SCRATCH_1: case ARIZONA_DSP2_SCRATCH_2: @@ -2479,6 +2521,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP3_STATUS_1: case ARIZONA_DSP3_STATUS_2: case ARIZONA_DSP3_STATUS_3: + case ARIZONA_DSP3_STATUS_4: + case ARIZONA_DSP3_WDMA_BUFFER_1: + case ARIZONA_DSP3_WDMA_BUFFER_2: + case ARIZONA_DSP3_WDMA_BUFFER_3: + case ARIZONA_DSP3_WDMA_BUFFER_4: + case ARIZONA_DSP3_WDMA_BUFFER_5: + case ARIZONA_DSP3_WDMA_BUFFER_6: + case ARIZONA_DSP3_WDMA_BUFFER_7: + case ARIZONA_DSP3_WDMA_BUFFER_8: + case ARIZONA_DSP3_RDMA_BUFFER_1: + case ARIZONA_DSP3_RDMA_BUFFER_2: + case ARIZONA_DSP3_RDMA_BUFFER_3: + case ARIZONA_DSP3_RDMA_BUFFER_4: + case ARIZONA_DSP3_RDMA_BUFFER_5: + case ARIZONA_DSP3_RDMA_BUFFER_6: + case ARIZONA_DSP3_WDMA_CONFIG_1: + case ARIZONA_DSP3_WDMA_CONFIG_2: + case ARIZONA_DSP3_WDMA_OFFSET_1: + case ARIZONA_DSP3_RDMA_CONFIG_1: + case ARIZONA_DSP3_RDMA_OFFSET_1: + case ARIZONA_DSP3_EXTERNAL_START_SELECT_1: case ARIZONA_DSP3_SCRATCH_0: case ARIZONA_DSP3_SCRATCH_1: case ARIZONA_DSP3_SCRATCH_2: @@ -2488,6 +2551,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP4_STATUS_1: case ARIZONA_DSP4_STATUS_2: case ARIZONA_DSP4_STATUS_3: + case ARIZONA_DSP4_STATUS_4: + case ARIZONA_DSP4_WDMA_BUFFER_1: + case ARIZONA_DSP4_WDMA_BUFFER_2: + case ARIZONA_DSP4_WDMA_BUFFER_3: + case ARIZONA_DSP4_WDMA_BUFFER_4: + case ARIZONA_DSP4_WDMA_BUFFER_5: + case ARIZONA_DSP4_WDMA_BUFFER_6: + case ARIZONA_DSP4_WDMA_BUFFER_7: + case ARIZONA_DSP4_WDMA_BUFFER_8: + case ARIZONA_DSP4_RDMA_BUFFER_1: + case ARIZONA_DSP4_RDMA_BUFFER_2: + case ARIZONA_DSP4_RDMA_BUFFER_3: + case ARIZONA_DSP4_RDMA_BUFFER_4: + case ARIZONA_DSP4_RDMA_BUFFER_5: + case ARIZONA_DSP4_RDMA_BUFFER_6: + case ARIZONA_DSP4_WDMA_CONFIG_1: + case ARIZONA_DSP4_WDMA_CONFIG_2: + case ARIZONA_DSP4_WDMA_OFFSET_1: + case ARIZONA_DSP4_RDMA_CONFIG_1: + case ARIZONA_DSP4_RDMA_OFFSET_1: + case ARIZONA_DSP4_EXTERNAL_START_SELECT_1: case ARIZONA_DSP4_SCRATCH_0: case ARIZONA_DSP4_SCRATCH_1: case ARIZONA_DSP4_SCRATCH_2: @@ -2543,6 +2627,27 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_STATUS_4: + case ARIZONA_DSP1_WDMA_BUFFER_1: + case ARIZONA_DSP1_WDMA_BUFFER_2: + case ARIZONA_DSP1_WDMA_BUFFER_3: + case ARIZONA_DSP1_WDMA_BUFFER_4: + case ARIZONA_DSP1_WDMA_BUFFER_5: + case ARIZONA_DSP1_WDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_BUFFER_7: + case ARIZONA_DSP1_WDMA_BUFFER_8: + case ARIZONA_DSP1_RDMA_BUFFER_1: + case ARIZONA_DSP1_RDMA_BUFFER_2: + case ARIZONA_DSP1_RDMA_BUFFER_3: + case ARIZONA_DSP1_RDMA_BUFFER_4: + case ARIZONA_DSP1_RDMA_BUFFER_5: + case ARIZONA_DSP1_RDMA_BUFFER_6: + case ARIZONA_DSP1_WDMA_CONFIG_1: + case ARIZONA_DSP1_WDMA_CONFIG_2: + case ARIZONA_DSP1_WDMA_OFFSET_1: + case ARIZONA_DSP1_RDMA_CONFIG_1: + case ARIZONA_DSP1_RDMA_OFFSET_1: + case ARIZONA_DSP1_EXTERNAL_START_SELECT_1: case ARIZONA_DSP1_SCRATCH_0: case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: @@ -2550,6 +2655,27 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP2_STATUS_1: case ARIZONA_DSP2_STATUS_2: case ARIZONA_DSP2_STATUS_3: + case ARIZONA_DSP2_STATUS_4: + case ARIZONA_DSP2_WDMA_BUFFER_1: + case ARIZONA_DSP2_WDMA_BUFFER_2: + case ARIZONA_DSP2_WDMA_BUFFER_3: + case ARIZONA_DSP2_WDMA_BUFFER_4: + case ARIZONA_DSP2_WDMA_BUFFER_5: + case ARIZONA_DSP2_WDMA_BUFFER_6: + case ARIZONA_DSP2_WDMA_BUFFER_7: + case ARIZONA_DSP2_WDMA_BUFFER_8: + case ARIZONA_DSP2_RDMA_BUFFER_1: + case ARIZONA_DSP2_RDMA_BUFFER_2: + case ARIZONA_DSP2_RDMA_BUFFER_3: + case ARIZONA_DSP2_RDMA_BUFFER_4: + case ARIZONA_DSP2_RDMA_BUFFER_5: + case ARIZONA_DSP2_RDMA_BUFFER_6: + case ARIZONA_DSP2_WDMA_CONFIG_1: + case ARIZONA_DSP2_WDMA_CONFIG_2: + case ARIZONA_DSP2_WDMA_OFFSET_1: + case ARIZONA_DSP2_RDMA_CONFIG_1: + case ARIZONA_DSP2_RDMA_OFFSET_1: + case ARIZONA_DSP2_EXTERNAL_START_SELECT_1: case ARIZONA_DSP2_SCRATCH_0: case ARIZONA_DSP2_SCRATCH_1: case ARIZONA_DSP2_SCRATCH_2: @@ -2557,6 +2683,27 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP3_STATUS_1: case ARIZONA_DSP3_STATUS_2: case ARIZONA_DSP3_STATUS_3: + case ARIZONA_DSP3_STATUS_4: + case ARIZONA_DSP3_WDMA_BUFFER_1: + case ARIZONA_DSP3_WDMA_BUFFER_2: + case ARIZONA_DSP3_WDMA_BUFFER_3: + case ARIZONA_DSP3_WDMA_BUFFER_4: + case ARIZONA_DSP3_WDMA_BUFFER_5: + case ARIZONA_DSP3_WDMA_BUFFER_6: + case ARIZONA_DSP3_WDMA_BUFFER_7: + case ARIZONA_DSP3_WDMA_BUFFER_8: + case ARIZONA_DSP3_RDMA_BUFFER_1: + case ARIZONA_DSP3_RDMA_BUFFER_2: + case ARIZONA_DSP3_RDMA_BUFFER_3: + case ARIZONA_DSP3_RDMA_BUFFER_4: + case ARIZONA_DSP3_RDMA_BUFFER_5: + case ARIZONA_DSP3_RDMA_BUFFER_6: + case ARIZONA_DSP3_WDMA_CONFIG_1: + case ARIZONA_DSP3_WDMA_CONFIG_2: + case ARIZONA_DSP3_WDMA_OFFSET_1: + case ARIZONA_DSP3_RDMA_CONFIG_1: + case ARIZONA_DSP3_RDMA_OFFSET_1: + case ARIZONA_DSP3_EXTERNAL_START_SELECT_1: case ARIZONA_DSP3_SCRATCH_0: case ARIZONA_DSP3_SCRATCH_1: case ARIZONA_DSP3_SCRATCH_2: @@ -2564,6 +2711,27 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP4_STATUS_1: case ARIZONA_DSP4_STATUS_2: case ARIZONA_DSP4_STATUS_3: + case ARIZONA_DSP4_STATUS_4: + case ARIZONA_DSP4_WDMA_BUFFER_1: + case ARIZONA_DSP4_WDMA_BUFFER_2: + case ARIZONA_DSP4_WDMA_BUFFER_3: + case ARIZONA_DSP4_WDMA_BUFFER_4: + case ARIZONA_DSP4_WDMA_BUFFER_5: + case ARIZONA_DSP4_WDMA_BUFFER_6: + case ARIZONA_DSP4_WDMA_BUFFER_7: + case ARIZONA_DSP4_WDMA_BUFFER_8: + case ARIZONA_DSP4_RDMA_BUFFER_1: + case ARIZONA_DSP4_RDMA_BUFFER_2: + case ARIZONA_DSP4_RDMA_BUFFER_3: + case ARIZONA_DSP4_RDMA_BUFFER_4: + case ARIZONA_DSP4_RDMA_BUFFER_5: + case ARIZONA_DSP4_RDMA_BUFFER_6: + case ARIZONA_DSP4_WDMA_CONFIG_1: + case ARIZONA_DSP4_WDMA_CONFIG_2: + case ARIZONA_DSP4_WDMA_OFFSET_1: + case ARIZONA_DSP4_RDMA_CONFIG_1: + case ARIZONA_DSP4_RDMA_OFFSET_1: + case ARIZONA_DSP4_EXTERNAL_START_SELECT_1: case ARIZONA_DSP4_SCRATCH_0: case ARIZONA_DSP4_SCRATCH_1: case ARIZONA_DSP4_SCRATCH_2: -- cgit v1.2.3 From a381b13e2aa064122325de9deaec51d6e4765ad7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Jan 2014 13:43:28 +0100 Subject: mfd: tc3589x: Reform device tree probing This changes the following mechanisms in the TC3589x device tree probing path: - Use the .of_match_table in struct device_driver to match the device in the device tree. - Add matches for the proper compatible strings "toshiba,..." and all sub-variants, just as is done for the .id matches. - Move over all the allocation of platform data etc to the tc3589x_of_probe() function and follow the pattern of passing a platform data pointer back, or an error pointer on error, as found in the STMPE driver. - Match the new (proper) compatible strings for the GPIO and keypad MFD cells. - Use of_device_is_compatible() rather than just !strcmp() to discover which cells to instantiate. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/tc3589x.c | 84 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 2cf636c267d9..bd83accc0f6d 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -13,8 +13,10 @@ #include #include #include +#include #include #include +#include /** * enum tc3589x_version - indicates the TC3589x version @@ -160,7 +162,7 @@ static const struct mfd_cell tc3589x_dev_gpio[] = { .name = "tc3589x-gpio", .num_resources = ARRAY_SIZE(gpio_resources), .resources = &gpio_resources[0], - .of_compatible = "tc3589x-gpio", + .of_compatible = "toshiba,tc3589x-gpio", }, }; @@ -169,7 +171,7 @@ static const struct mfd_cell tc3589x_dev_keypad[] = { .name = "tc3589x-keypad", .num_resources = ARRAY_SIZE(keypad_resources), .resources = &keypad_resources[0], - .of_compatible = "tc3589x-keypad", + .of_compatible = "toshiba,tc3589x-keypad", }, }; @@ -318,45 +320,74 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) return ret; } -static int tc3589x_of_probe(struct device_node *np, - struct tc3589x_platform_data *pdata) +#ifdef CONFIG_OF +static const struct of_device_id tc3589x_match[] = { + /* Legacy compatible string */ + { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, + { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 }, + { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 }, + { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 }, + { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 }, + { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 }, + { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 }, + { } +}; + +MODULE_DEVICE_TABLE(of, tc3589x_match); + +static struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) { + struct device_node *np = dev->of_node; + struct tc3589x_platform_data *pdata; struct device_node *child; + const struct of_device_id *of_id; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + of_id = of_match_device(tc3589x_match, dev); + if (!of_id) + return ERR_PTR(-ENODEV); + *version = (enum tc3589x_version) of_id->data; for_each_child_of_node(np, child) { - if (!strcmp(child->name, "tc3589x_gpio")) { + if (of_device_is_compatible(child, "toshiba,tc3589x-gpio")) pdata->block |= TC3589x_BLOCK_GPIO; - } - if (!strcmp(child->name, "tc3589x_keypad")) { + if (of_device_is_compatible(child, "toshiba,tc3589x-keypad")) pdata->block |= TC3589x_BLOCK_KEYPAD; - } } - return 0; + return pdata; } +#else +static inline struct tc3589x_platform_data * +tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) +{ + dev_err(dev, "no device tree support\n"); + return ERR_PTR(-ENODEV); +} +#endif static int tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); struct device_node *np = i2c->dev.of_node; + struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); struct tc3589x *tc3589x; + enum tc3589x_version version; int ret; if (!pdata) { - if (np) { - pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - ret = tc3589x_of_probe(np, pdata); - if (ret) - return ret; - } - else { + pdata = tc3589x_of_probe(&i2c->dev, &version); + if (IS_ERR(pdata)) { dev_err(&i2c->dev, "No platform data or DT found\n"); - return -EINVAL; + return PTR_ERR(pdata); } + } else { + /* When not probing from device tree we have this ID */ + version = id->driver_data; } if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA @@ -375,7 +406,7 @@ static int tc3589x_probe(struct i2c_client *i2c, tc3589x->pdata = pdata; tc3589x->irq_base = pdata->irq_base; - switch (id->driver_data) { + switch (version) { case TC3589X_TC35893: case TC3589X_TC35895: case TC3589X_TC35896: @@ -471,9 +502,12 @@ static const struct i2c_device_id tc3589x_id[] = { MODULE_DEVICE_TABLE(i2c, tc3589x_id); static struct i2c_driver tc3589x_driver = { - .driver.name = "tc3589x", - .driver.owner = THIS_MODULE, - .driver.pm = &tc3589x_dev_pm_ops, + .driver = { + .name = "tc3589x", + .owner = THIS_MODULE, + .pm = &tc3589x_dev_pm_ops, + .of_match_table = of_match_ptr(tc3589x_match), + }, .probe = tc3589x_probe, .remove = tc3589x_remove, .id_table = tc3589x_id, -- cgit v1.2.3 From 44b4dc616365d7897808555d415099330e3af9df Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 6 Feb 2014 11:20:12 +0530 Subject: mfd: tps65218: Add driver for the TPS65218 PMIC The TPS65218 chip is a power management IC for Portable Navigation Systems and Tablet Computing devices. It contains the following components: - Regulators. - Over Temperature warning and Shut down. This patch adds support for tps65218 mfd device. At this time only the regulator functionality is made available. Signed-off-by: Keerthy Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 15 +++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65218.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/mfd/tps65218.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 30fbacfe8b3b..3e2d698ac432 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -853,6 +853,21 @@ config MFD_TPS65217 This driver can also be built as a module. If so, the module will be called tps65217. +config MFD_TPS65218 + tristate "TI TPS65218 Power Management chips" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + If you say yes here you get support for the TPS65218 series of + Power Management chips. + These include voltage regulators, gpio and other features + that are often used in portable devices. Only regulator + component is currently supported. + + This driver can also be built as a module. If so, the module + will be called tps65218. + config MFD_TPS6586X bool "TI TPS6586x Power Management chips" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c8eb0bcf4da0..5e5cfbd1b8da 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o +obj-$(CONFIG_MFD_TPS65218) += tps65218.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c new file mode 100644 index 000000000000..a74bfb59f18f --- /dev/null +++ b/drivers/mfd/tps65218.c @@ -0,0 +1,282 @@ +/* + * Driver for TPS65218 Integrated power management chipsets + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TPS65218_PASSWORD_REGS_UNLOCK 0x7D + +/** + * tps65218_reg_read: Read a single tps65218 register. + * + * @tps: Device to read from. + * @reg: Register to read. + * @val: Contians the value + */ +int tps65218_reg_read(struct tps65218 *tps, unsigned int reg, + unsigned int *val) +{ + return regmap_read(tps->regmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65218_reg_read); + +/** + * tps65218_reg_write: Write a single tps65218 register. + * + * @tps65218: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + * @level: Password protected level + */ +int tps65218_reg_write(struct tps65218 *tps, unsigned int reg, + unsigned int val, unsigned int level) +{ + int ret; + unsigned int xor_reg_val; + + switch (level) { + case TPS65218_PROTECT_NONE: + return regmap_write(tps->regmap, reg, val); + case TPS65218_PROTECT_L1: + xor_reg_val = reg ^ TPS65218_PASSWORD_REGS_UNLOCK; + ret = regmap_write(tps->regmap, TPS65218_REG_PASSWORD, + xor_reg_val); + if (ret < 0) + return ret; + + return regmap_write(tps->regmap, reg, val); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(tps65218_reg_write); + +/** + * tps65218_update_bits: Modify bits w.r.t mask, val and level. + * + * @tps65218: Device to write to. + * @reg: Register to read-write to. + * @mask: Mask. + * @val: Value to write. + * @level: Password protected level + */ +static int tps65218_update_bits(struct tps65218 *tps, unsigned int reg, + unsigned int mask, unsigned int val, unsigned int level) +{ + int ret; + unsigned int data; + + ret = tps65218_reg_read(tps, reg, &data); + if (ret) { + dev_err(tps->dev, "Read from reg 0x%x failed\n", reg); + return ret; + } + + data &= ~mask; + data |= val & mask; + + mutex_lock(&tps->tps_lock); + ret = tps65218_reg_write(tps, reg, data, level); + if (ret) + dev_err(tps->dev, "Write for reg 0x%x failed\n", reg); + mutex_unlock(&tps->tps_lock); + + return ret; +} + +int tps65218_set_bits(struct tps65218 *tps, unsigned int reg, + unsigned int mask, unsigned int val, unsigned int level) +{ + return tps65218_update_bits(tps, reg, mask, val, level); +} +EXPORT_SYMBOL_GPL(tps65218_set_bits); + +int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg, + unsigned int mask, unsigned int level) +{ + return tps65218_update_bits(tps, reg, mask, 0, level); +} +EXPORT_SYMBOL_GPL(tps65218_clear_bits); + +static struct regmap_config tps65218_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_irq tps65218_irqs[] = { + /* INT1 IRQs */ + [TPS65218_PRGC_IRQ] = { + .mask = TPS65218_INT1_PRGC, + }, + [TPS65218_CC_AQC_IRQ] = { + .mask = TPS65218_INT1_CC_AQC, + }, + [TPS65218_HOT_IRQ] = { + .mask = TPS65218_INT1_HOT, + }, + [TPS65218_PB_IRQ] = { + .mask = TPS65218_INT1_PB, + }, + [TPS65218_AC_IRQ] = { + .mask = TPS65218_INT1_AC, + }, + [TPS65218_VPRG_IRQ] = { + .mask = TPS65218_INT1_VPRG, + }, + [TPS65218_INVALID1_IRQ] = { + }, + [TPS65218_INVALID2_IRQ] = { + }, + /* INT2 IRQs*/ + [TPS65218_LS1_I_IRQ] = { + .mask = TPS65218_INT2_LS1_I, + .reg_offset = 1, + }, + [TPS65218_LS2_I_IRQ] = { + .mask = TPS65218_INT2_LS2_I, + .reg_offset = 1, + }, + [TPS65218_LS3_I_IRQ] = { + .mask = TPS65218_INT2_LS3_I, + .reg_offset = 1, + }, + [TPS65218_LS1_F_IRQ] = { + .mask = TPS65218_INT2_LS1_F, + .reg_offset = 1, + }, + [TPS65218_LS2_F_IRQ] = { + .mask = TPS65218_INT2_LS2_F, + .reg_offset = 1, + }, + [TPS65218_LS3_F_IRQ] = { + .mask = TPS65218_INT2_LS3_F, + .reg_offset = 1, + }, + [TPS65218_INVALID3_IRQ] = { + }, + [TPS65218_INVALID4_IRQ] = { + }, +}; + +static struct regmap_irq_chip tps65218_irq_chip = { + .name = "tps65218", + .irqs = tps65218_irqs, + .num_irqs = ARRAY_SIZE(tps65218_irqs), + + .num_regs = 2, + .mask_base = TPS65218_REG_INT_MASK1, +}; + +static const struct of_device_id of_tps65218_match_table[] = { + { .compatible = "ti,tps65218", }, +}; + +static int tps65218_probe(struct i2c_client *client, + const struct i2c_device_id *ids) +{ + struct tps65218 *tps; + const struct of_device_id *match; + int ret; + + match = of_match_device(of_tps65218_match_table, &client->dev); + if (!match) { + dev_err(&client->dev, + "Failed to find matching dt id\n"); + return -EINVAL; + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + i2c_set_clientdata(client, tps); + tps->dev = &client->dev; + tps->irq = client->irq; + tps->regmap = devm_regmap_init_i2c(client, &tps65218_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(tps->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + mutex_init(&tps->tps_lock); + + ret = regmap_add_irq_chip(tps->regmap, tps->irq, + IRQF_ONESHOT, 0, &tps65218_irq_chip, + &tps->irq_data); + if (ret < 0) + return ret; + + ret = of_platform_populate(client->dev.of_node, NULL, NULL, + &client->dev); + if (ret < 0) + goto err_irq; + + return 0; + +err_irq: + regmap_del_irq_chip(tps->irq, tps->irq_data); + + return ret; +} + +static int tps65218_remove(struct i2c_client *client) +{ + struct tps65218 *tps = i2c_get_clientdata(client); + + regmap_del_irq_chip(tps->irq, tps->irq_data); + + return 0; +} + +static const struct i2c_device_id tps65218_id_table[] = { + { "tps65218", TPS65218 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tps65218_id_table); + +static struct i2c_driver tps65218_driver = { + .driver = { + .name = "tps65218", + .owner = THIS_MODULE, + .of_match_table = of_tps65218_match_table, + }, + .probe = tps65218_probe, + .remove = tps65218_remove, + .id_table = tps65218_id_table, +}; + +module_i2c_driver(tps65218_driver); + +MODULE_AUTHOR("J Keerthy "); +MODULE_DESCRIPTION("TPS65218 chip family multi-function driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 52397fe18d6b7ae377293bdcf9827185833c8144 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 4 Feb 2014 14:37:13 +0530 Subject: mfd: stmpe: Trivial: Remove unnecessary semicolon Semicolon is not necessary after the while statement. Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/stmpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 42ccd0544513..4a91f6771fb8 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -706,7 +706,7 @@ static int stmpe1801_reset(struct stmpe *stmpe) if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET)) return 0; usleep_range(100, 200); - }; + } return -EIO; } -- cgit v1.2.3 From 3c699105d0376c14940ce7cf561754a94cdff8dd Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 21 Jan 2014 16:23:01 -0500 Subject: mfd: Delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Signed-off-by: Paul Gortmaker Signed-off-by: Lee Jones --- drivers/iio/adc/twl4030-madc.c | 1 - drivers/mfd/adp5520.c | 1 - drivers/mfd/cs5535-mfd.c | 1 - drivers/mfd/janz-cmodio.c | 1 - drivers/mfd/lpc_ich.c | 1 - drivers/mfd/lpc_sch.c | 1 - drivers/mfd/mcp-sa11x0.c | 1 - drivers/mfd/pcf50633-adc.c | 1 - drivers/mfd/rc5t583-irq.c | 1 - drivers/mfd/rdc321x-southbridge.c | 1 - drivers/mfd/retu-mfd.c | 1 - drivers/mfd/smsc-ece1099.c | 1 - drivers/mfd/ti-ssp.c | 1 - drivers/mfd/ti_am335x_tscadc.c | 1 - drivers/mfd/tps65912-core.c | 1 - drivers/mfd/tps65912-irq.c | 1 - drivers/mfd/twl4030-irq.c | 1 - drivers/mfd/twl6030-irq.c | 1 - drivers/mfd/vexpress-config.c | 1 - drivers/mfd/wm8350-core.c | 1 - drivers/mfd/wm8350-irq.c | 1 - 21 files changed, 21 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 8a014fbb98bc..7de1c4c87942 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -29,7 +29,6 @@ * */ -#include #include #include #include diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 62501553d63c..f495b8b57dd7 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 17c13012686a..be91cb5d6e78 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -23,7 +23,6 @@ */ #include -#include #include #include #include diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 81b7d88af313..433f823037dd 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index be93fa261ded..010c1b490478 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -58,7 +58,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 3bb05c03c68d..4ee755034f3b 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -23,7 +23,6 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 41c31b3ac940..29d76986b40b 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -12,7 +12,6 @@ * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. */ #include -#include #include #include #include diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c index b8941a556d71..c1984b0d1b65 100644 --- a/drivers/mfd/pcf50633-adc.c +++ b/drivers/mfd/pcf50633-adc.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c index b41db5968706..bb8502020274 100644 --- a/drivers/mfd/rc5t583-irq.c +++ b/drivers/mfd/rc5t583-irq.c @@ -22,7 +22,6 @@ */ #include #include -#include #include #include diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index d346146249a2..c79569750be9 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -19,7 +19,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ -#include #include #include #include diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index c8f345f7e9a2..663f8a37aa6b 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c index 24ae3d8421c5..90112d4cc905 100644 --- a/drivers/mfd/smsc-ece1099.c +++ b/drivers/mfd/smsc-ece1099.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c index a5424579679c..0769ecdd2b7f 100644 --- a/drivers/mfd/ti-ssp.c +++ b/drivers/mfd/ti-ssp.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index d4e860413bb5..6000c49a352c 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 27a518e0eec6..1f82d60b1d0f 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c index d360a83a2738..fbecec7f1e3d 100644 --- a/drivers/mfd/tps65912-irq.c +++ b/drivers/mfd/tps65912-irq.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 9aa6d1efa241..596b1f657e21 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -27,7 +27,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 18a607e2ca06..a6bb17d908b8 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -31,7 +31,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c index 84ce6b9daa3d..0c9c3500e25d 100644 --- a/drivers/mfd/vexpress-config.c +++ b/drivers/mfd/vexpress-config.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 7c1ae24605d9..4ab527f5c53b 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 624ff90501cd..cd01f7962dfd 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 97dc4ed3fa377ec91bb60ba98b70d645c2099384 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:34 +0100 Subject: mfd: max8997: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C devices for RTC, haptic and MUIC with i2c_new_dummy() but it does not check the return value of this calls. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by i2c_unregister_device(). If i2c_new_dummy() fails for RTC, haptic or MUIC devices, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max8997.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 5adede0fb04c..8cf7a015cfe5 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -208,10 +208,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c, mutex_init(&max8997->iolock); max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + if (!max8997->rtc) { + dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } i2c_set_clientdata(max8997->rtc, max8997); + max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + if (!max8997->haptic) { + dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n"); + ret = -ENODEV; + goto err_i2c_haptic; + } i2c_set_clientdata(max8997->haptic, max8997); + max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + if (!max8997->muic) { + dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n"); + ret = -ENODEV; + goto err_i2c_muic; + } i2c_set_clientdata(max8997->muic, max8997); pm_runtime_set_active(max8997->dev); @@ -239,7 +255,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c, err_mfd: mfd_remove_devices(max8997->dev); i2c_unregister_device(max8997->muic); +err_i2c_muic: i2c_unregister_device(max8997->haptic); +err_i2c_haptic: i2c_unregister_device(max8997->rtc); return ret; } -- cgit v1.2.3 From b9e183a1d495cd65412abe0f9df19b151716bfe7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:31 +0100 Subject: mfd: max77686: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C device for RTC with i2c_new_dummy() but it does not check the return value of this call. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by i2c_unregister_device(). If i2c_new_dummy() fails for RTC device, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index f53d5823a3f7..e5fce765accb 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -121,6 +121,10 @@ static int max77686_i2c_probe(struct i2c_client *i2c, dev_info(max77686->dev, "device found\n"); max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + if (!max77686->rtc) { + dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } i2c_set_clientdata(max77686->rtc, max77686); max77686_irq_init(max77686); -- cgit v1.2.3 From ed26f87b9f71693a1d1ee85f5e6209601505080f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:35 +0100 Subject: mfd: max8998: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C device for RTC with i2c_new_dummy() but it does not check the return value of this call. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by i2c_unregister_device(). If i2c_new_dummy() fails for RTC device, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max8998.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 5d5e186b5d8b..592db06098e6 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -215,6 +215,10 @@ static int max8998_i2c_probe(struct i2c_client *i2c, mutex_init(&max8998->iolock); max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); + if (!max8998->rtc) { + dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } i2c_set_clientdata(max8998->rtc, max8998); max8998_irq_init(max8998); -- cgit v1.2.3 From 96cf3dedc491d2f1f66cc26217f2b06b0c7b6797 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:33 +0100 Subject: mfd: max8925: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C devices for RTC and ADC with i2c_new_dummy() but it does not check the return value of this calls. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by i2c_unregister_device(). If i2c_new_dummy() fails for RTC or ADC devices, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max8925-i2c.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index 176aa26fc787..a83eed5c15ca 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -181,9 +181,18 @@ static int max8925_probe(struct i2c_client *client, mutex_init(&chip->io_lock); chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR); + if (!chip->rtc) { + dev_err(chip->dev, "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } i2c_set_clientdata(chip->rtc, chip); chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR); + if (!chip->adc) { + dev_err(chip->dev, "Failed to allocate I2C device for ADC\n"); + i2c_unregister_device(chip->rtc); + return -ENODEV; + } i2c_set_clientdata(chip->adc, chip); device_init_wakeup(&client->dev, 1); -- cgit v1.2.3 From a7ab1c8b261305af583ce26bb4a14f555fdaa73e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:30 +0100 Subject: mfd: 88pm860x: Fix I2C device resource leak on regmap init fail During probe the driver allocates dummy I2C device for companion chip and then allocates a regmap for it. If regmap_init_i2c() fails then the I2C driver (allocated with i2c_new_dummy()) is not freed and this resource leaks. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/88pm860x-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index c9b1f6422941..2461014a4ad5 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1185,6 +1185,7 @@ static int pm860x_probe(struct i2c_client *client, ret = PTR_ERR(chip->regmap_companion); dev_err(&chip->companion->dev, "Failed to allocate register map: %d\n", ret); + i2c_unregister_device(chip->companion); return ret; } i2c_set_clientdata(chip->companion, chip); -- cgit v1.2.3 From 159ce52a6b777fc82fa0b51c7440e25f9e4c6feb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:29 +0100 Subject: mfd: 88pm860x: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C device for companion chip with i2c_new_dummy() but it does not check the return value of this call. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by regmap_init_i2c(). If i2c_new_dummy() fails for companion device, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/88pm860x-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 2461014a4ad5..bcfc9e85b4a0 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1179,6 +1179,11 @@ static int pm860x_probe(struct i2c_client *client, chip->companion_addr = pdata->companion_addr; chip->companion = i2c_new_dummy(chip->client->adapter, chip->companion_addr); + if (!chip->companion) { + dev_err(&client->dev, + "Failed to allocate I2C companion device\n"); + return -ENODEV; + } chip->regmap_companion = regmap_init_i2c(chip->companion, &pm860x_regmap_config); if (IS_ERR(chip->regmap_companion)) { -- cgit v1.2.3 From ad09dd6a1f5d6244bd89314015af506ba7f9810a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Feb 2014 11:03:32 +0100 Subject: mfd: max77693: Fix possible NULL pointer dereference on i2c_new_dummy error During probe the driver allocates dummy I2C devices for MUIC and haptic with i2c_new_dummy() but it does not check the return value of this calls. In case of error (i2c_new_device(): memory allocation failure or I2C address cannot be used) this function returns NULL which is later used by devm_regmap_init_i2c() and i2c_unregister_device(). If i2c_new_dummy() fails for MUIC or haptic devices, fail also the probe for main MFD driver. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max77693.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index e0859987ab6b..c5535f018466 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -148,9 +148,18 @@ static int max77693_i2c_probe(struct i2c_client *i2c, dev_info(max77693->dev, "device ID: 0x%x\n", reg_data); max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + if (!max77693->muic) { + dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n"); + return -ENODEV; + } i2c_set_clientdata(max77693->muic, max77693); max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + if (!max77693->haptic) { + dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n"); + ret = -ENODEV; + goto err_i2c_haptic; + } i2c_set_clientdata(max77693->haptic, max77693); /* @@ -184,8 +193,9 @@ err_mfd: max77693_irq_exit(max77693); err_irq: err_regmap_muic: - i2c_unregister_device(max77693->muic); i2c_unregister_device(max77693->haptic); +err_i2c_haptic: + i2c_unregister_device(max77693->muic); return ret; } -- cgit v1.2.3 From bcb5a7d335da73a798d8a8f6ce9a8605d25426f4 Mon Sep 17 00:00:00 2001 From: "anthony.olech.opensource@diasemi.com" Date: Thu, 6 Feb 2014 17:33:24 +0000 Subject: mfd: da9052: Fix volatile register definition ommissions Three of the PMIC registers have some bits that are changed autonomously by the PMIC itself (some time) after being set by some component driver of the DA9052 PMIC and hence they need to be marked as volatile so that the regmap API will not cache their values. Acked-by: David Dajun Chen Signed-off-by: Anthony Olech Signed-off-by: Lee Jones --- drivers/mfd/da9052-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 25838f10b35b..e8af816d73a9 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -279,6 +279,9 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg) case DA9052_EVENT_B_REG: case DA9052_EVENT_C_REG: case DA9052_EVENT_D_REG: + case DA9052_CONTROL_B_REG: + case DA9052_CONTROL_D_REG: + case DA9052_SUPPLY_REG: case DA9052_FAULTLOG_REG: case DA9052_CHG_TIME_REG: case DA9052_ADC_RES_L_REG: -- cgit v1.2.3 From 924ff918ad8c7e5665b05d9c7b258623c88d65c5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 12 Feb 2014 14:31:49 +0900 Subject: mfd: ti_am335x_tscadc: Use devm_ioremap_resource() Use devm_ioremap_resource() in order to make the code simpler, and remove redundant return value check of platform_get_resource() because the value is checked by devm_ioremap_resource(). Signed-off-by: Jingoo Han Signed-off-by: Lee Jones --- drivers/mfd/ti_am335x_tscadc.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 6000c49a352c..dd4bf5816221 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -183,12 +183,6 @@ static int ti_tscadc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined.\n"); - return -EINVAL; - } - /* Allocate memory for device */ tscadc = devm_kzalloc(&pdev->dev, sizeof(struct ti_tscadc_dev), GFP_KERNEL); @@ -205,19 +199,10 @@ static int ti_tscadc_probe(struct platform_device *pdev) } else tscadc->irq = err; - res = devm_request_mem_region(&pdev->dev, - res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to reserve registers.\n"); - return -EBUSY; - } - - tscadc->tscadc_base = devm_ioremap(&pdev->dev, - res->start, resource_size(res)); - if (!tscadc->tscadc_base) { - dev_err(&pdev->dev, "failed to map registers.\n"); - return -ENOMEM; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tscadc->tscadc_base)) + return PTR_ERR(tscadc->tscadc_base); tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, tscadc->tscadc_base, &tscadc_regmap_config); -- cgit v1.2.3 From 921a1b0c1a84f482b3643025ae47e36f6849fb1e Mon Sep 17 00:00:00 2001 From: Laszlo Papp Date: Tue, 11 Feb 2014 04:50:51 +0000 Subject: mfd: Trivial: Fix a grammar issues in MFD Kconfig s/to support for/to add support for/ Signed-off-by: Laszlo Papp Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3e2d698ac432..91fea6164bce 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -100,7 +100,7 @@ config PMIC_DA903X bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" depends on I2C=y help - Say yes here to support for Dialog Semiconductor DA9030 (a.k.a + Say yes here to add support for Dialog Semiconductor DA9030 (a.k.a ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC usually found on PXA processors-based platforms. This includes the I2C driver and the core APIs _only_, you have to select @@ -324,7 +324,7 @@ config MFD_MAX14577 select REGMAP_I2C select IRQ_DOMAIN help - Say yes here to support for Maxim Semiconductor MAX14577. + Say yes here to add support for Maxim Semiconductor MAX14577. This is a Micro-USB IC with Charger controls on chip. This driver provides common support for accessing the device; additional drivers must be enabled in order to use the functionality @@ -337,7 +337,7 @@ config MFD_MAX77686 select REGMAP_I2C select IRQ_DOMAIN help - Say yes here to support for Maxim Semiconductor MAX77686. + Say yes here to add support for Maxim Semiconductor MAX77686. This is a Power Management IC with RTC on chip. This driver provides common support for accessing the device; additional drivers must be enabled in order to use the functionality @@ -349,7 +349,7 @@ config MFD_MAX77693 select MFD_CORE select REGMAP_I2C help - Say yes here to support for Maxim Semiconductor MAX77693. + Say yes here to add support for Maxim Semiconductor MAX77693. This is a companion Power Management IC with Flash, Haptic, Charger, and MUIC(Micro USB Interface Controller) controls on chip. This driver provides common support for accessing the device; @@ -363,7 +363,7 @@ config MFD_MAX8907 select REGMAP_I2C select REGMAP_IRQ help - Say yes here to support for Maxim Semiconductor MAX8907. This is + Say yes here to add support for Maxim Semiconductor MAX8907. This is a Power Management IC. This driver provides common support for accessing the device; additional drivers must be enabled in order to use the functionality of the device. @@ -373,7 +373,7 @@ config MFD_MAX8925 depends on I2C=y select MFD_CORE help - Say yes here to support for Maxim Semiconductor MAX8925. This is + Say yes here to add support for Maxim Semiconductor MAX8925. This is a Power Management IC. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality of the device. @@ -384,7 +384,7 @@ config MFD_MAX8997 select MFD_CORE select IRQ_DOMAIN help - Say yes here to support for Maxim Semiconductor MAX8997/8966. + Say yes here to add support for Maxim Semiconductor MAX8997/8966. This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, MUIC controls on chip. This driver provides common support for accessing the device; @@ -397,7 +397,7 @@ config MFD_MAX8998 select MFD_CORE select IRQ_DOMAIN help - Say yes here to support for Maxim Semiconductor MAX8998 and + Say yes here to add support for Maxim Semiconductor MAX8998 and National Semiconductor LP3974. This is a Power Management IC. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality -- cgit v1.2.3 From 141050cf3d84fc303df58796d68dc1376b0e8f67 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Feb 2014 11:10:56 +0100 Subject: mfd: 88pm800: Fix I2C device resource leak if probe fails During probe the driver allocates two dummy I2C devices for subchips in function pm800_pages_init(). Additionally this function allocates regmaps for these subchips. If any of these steps fail then these dummy I2C devices are not freed and resources leak. On pm800_pages_init() fail the driver must call pm800_pages_exit() to unregister dummy I2C devices. Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/88pm800.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index 7dca1e640970..841717a2842c 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -571,7 +571,7 @@ static int pm800_probe(struct i2c_client *client, ret = pm800_pages_init(chip); if (ret) { dev_err(&client->dev, "pm800_pages_init failed!\n"); - goto err_page_init; + goto err_device_init; } ret = device_800_init(chip, pdata); @@ -587,7 +587,6 @@ static int pm800_probe(struct i2c_client *client, err_device_init: pm800_pages_exit(chip); -err_page_init: err_subchip_alloc: pm80x_deinit(); out_init: -- cgit v1.2.3 From 63f5d2e8b227d54d6e0a750a4b9670a899fa1a05 Mon Sep 17 00:00:00 2001 From: Semen Protsenko Date: Thu, 13 Feb 2014 18:39:54 +0200 Subject: mfd: vexpress-sysreg: Initialize 'site' variable 'site' variable should be initialized with 0 so that when 'site' property doesn't exist in DTB it can be handled correctly. '0' value means board site number is motherboard (see Documentation/devicetree/bindings/arm/vexpress.txt for details). Signed-off-by: Semen Protsenko Signed-off-by: Lee Jones --- drivers/mfd/vexpress-sysreg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 981bef4b7ebc..35281e804e7e 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -168,7 +168,7 @@ static void *vexpress_sysreg_config_func_get(struct device *dev, struct device_node *node) { struct vexpress_sysreg_config_func *config_func; - u32 site; + u32 site = 0; u32 position = 0; u32 dcc = 0; u32 func_device[2]; -- cgit v1.2.3 From c8016d45a3520fb6bd41f5740f075b53df280683 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Feb 2014 14:40:11 +0530 Subject: mfd: max14577: Include missing err.h Add this header explicitly for IS_ERR and friends. Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index c9859d1baf14..5f13cefe8def 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -18,6 +18,7 @@ * This driver is based on max8997.c */ +#include #include #include #include -- cgit v1.2.3 From e2f3e9bbbaa64a017d91b33fdd9d0886f431b36b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Feb 2014 14:40:12 +0530 Subject: mfd: stw481x: Staticize stw481x_regmap_config stw481x_regmap_config is local to this file. Acked-by: Linus Walleij Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/stw481x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c index 1243d5c6a448..cc42f88586f6 100644 --- a/drivers/mfd/stw481x.c +++ b/drivers/mfd/stw481x.c @@ -167,7 +167,7 @@ static struct mfd_cell stw481x_cells[] = { }, }; -const struct regmap_config stw481x_regmap_config = { +static const struct regmap_config stw481x_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -- cgit v1.2.3 From c88fd91bcd016b84e5f7d7ebd583e073e5ead48a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Feb 2014 14:40:13 +0530 Subject: mfd: stw481x: Check the return value of devm_regmap_init_i2c devm_regmap_init_i2c can fail. Check for it. Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/stw481x.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c index cc42f88586f6..7ceb3df09e25 100644 --- a/drivers/mfd/stw481x.c +++ b/drivers/mfd/stw481x.c @@ -186,6 +186,12 @@ static int stw481x_probe(struct i2c_client *client, i2c_set_clientdata(client, stw481x); stw481x->client = client; stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config); + if (IS_ERR(stw481x->map)) { + ret = PTR_ERR(stw481x->map); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } ret = stw481x_startup(stw481x); if (ret) { -- cgit v1.2.3 From 730876be256603b4ee7225a125467d97a7ce9060 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Wed, 12 Feb 2014 18:00:36 +0800 Subject: mfd: Add realtek USB card reader driver Realtek USB card reader provides a channel to transfer command or data to flash memory cards. This driver exports host instances for mmc and memstick subsystems and handles basic works. Acked-by: Greg Kroah-Hartman Signed-off-by: Roger Tseng Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/rtsx_usb.c | 760 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 771 insertions(+) create mode 100644 drivers/mfd/rtsx_usb.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 91fea6164bce..09fb5706208f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -516,6 +516,16 @@ config MFD_RTSX_PCI types of memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard. +config MFD_RTSX_USB + tristate "Realtek USB card reader" + depends on USB + select MFD_CORE + help + Select this option to get support for Realtek USB 2.0 card readers + including RTS5129, RTS5139, RTS5179 and RTS5170. + Realtek card reader supports access to many types of memory cards, + such as Memory Stick Pro, Secure Digital and MultiMediaCard. + config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5e5cfbd1b8da..377bd519884c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o +obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c new file mode 100644 index 000000000000..b53b9d46cc45 --- /dev/null +++ b/drivers/mfd/rtsx_usb.c @@ -0,0 +1,760 @@ +/* Driver for Realtek USB card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Roger Tseng + */ +#include +#include +#include +#include +#include +#include +#include + +static int polling_pipe = 1; +module_param(polling_pipe, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); + +static struct mfd_cell rtsx_usb_cells[] = { + [RTSX_USB_SD_CARD] = { + .name = "rtsx_usb_sdmmc", + .pdata_size = 0, + }, + [RTSX_USB_MS_CARD] = { + .name = "rtsx_usb_ms", + .pdata_size = 0, + }, +}; + +static void rtsx_usb_sg_timed_out(unsigned long data) +{ + struct rtsx_ucr *ucr = (struct rtsx_ucr *)data; + + dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); + usb_sg_cancel(&ucr->current_sg); + + /* we know the cancellation is caused by time-out */ + ucr->current_sg.status = -ETIMEDOUT; +} + +static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, + unsigned int pipe, struct scatterlist *sg, int num_sg, + unsigned int length, unsigned int *act_len, int timeout) +{ + int ret; + + dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", + __func__, length, num_sg); + ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, + sg, num_sg, length, GFP_NOIO); + if (ret) + return ret; + + ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); + add_timer(&ucr->sg_timer); + usb_sg_wait(&ucr->current_sg); + del_timer(&ucr->sg_timer); + + if (act_len) + *act_len = ucr->current_sg.bytes; + + return ucr->current_sg.status; +} + +int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, + void *buf, unsigned int len, int num_sg, + unsigned int *act_len, int timeout) +{ + if (timeout < 600) + timeout = 600; + + if (num_sg) + return rtsx_usb_bulk_transfer_sglist(ucr, pipe, + (struct scatterlist *)buf, num_sg, len, act_len, + timeout); + else + return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, + timeout); +} +EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); + +static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, + u16 addr, u16 len, u8 seq_type) +{ + rtsx_usb_cmd_hdr_tag(ucr); + + ucr->cmd_buf[PACKET_TYPE] = seq_type; + ucr->cmd_buf[5] = (u8)(len >> 8); + ucr->cmd_buf[6] = (u8)len; + ucr->cmd_buf[8] = (u8)(addr >> 8); + ucr->cmd_buf[9] = (u8)addr; + + if (seq_type == SEQ_WRITE) + ucr->cmd_buf[STAGE_FLAG] = 0; + else + ucr->cmd_buf[STAGE_FLAG] = STAGE_R; +} + +static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, + u16 addr, u16 len, u8 *data) +{ + u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); + + if (!data) + return -EINVAL; + + if (cmd_len > IOBUF_SIZE) + return -EINVAL; + + rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); + memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); + + return rtsx_usb_transfer_data(ucr, + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), + ucr->cmd_buf, cmd_len, 0, NULL, 100); +} + +static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, + u16 addr, u16 len, u8 *data) +{ + int i, ret; + u16 rsp_len = round_down(len, 4); + u16 res_len = len - rsp_len; + + if (!data) + return -EINVAL; + + /* 4-byte aligned part */ + if (rsp_len) { + rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); + ret = rtsx_usb_transfer_data(ucr, + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), + ucr->cmd_buf, 12, 0, NULL, 100); + if (ret) + return ret; + + ret = rtsx_usb_transfer_data(ucr, + usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), + data, rsp_len, 0, NULL, 100); + if (ret) + return ret; + } + + /* unaligned part */ + for (i = 0; i < res_len; i++) { + ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, + data + rsp_len + i); + if (ret) + return ret; + } + + return 0; +} + +int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) +{ + return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); +} +EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); + +int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) +{ + return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); +} +EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); + +int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, + u8 mask, u8 data) +{ + u16 value, index; + + addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; + value = swab16(addr); + index = mask | data << 8; + + return usb_control_msg(ucr->pusb_dev, + usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); + +int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) +{ + u16 value; + + if (!data) + return -EINVAL; + *data = 0; + + addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; + value = swab16(addr); + + return usb_control_msg(ucr->pusb_dev, + usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, data, 1, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); + +void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, + u8 mask, u8 data) +{ + int i; + + if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { + i = CMD_OFFSET + ucr->cmd_idx * 4; + + ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | + (u8)((reg_addr >> 8) & 0x3F); + ucr->cmd_buf[i++] = (u8)reg_addr; + ucr->cmd_buf[i++] = mask; + ucr->cmd_buf[i++] = data; + + ucr->cmd_idx++; + } +} +EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); + +int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) +{ + int ret; + + ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); + ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); + ucr->cmd_buf[STAGE_FLAG] = flag; + + ret = rtsx_usb_transfer_data(ucr, + usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), + ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, + 0, NULL, timeout); + if (ret) { + rtsx_usb_clear_fsm_err(ucr); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); + +int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) +{ + if (rsp_len <= 0) + return -EINVAL; + + rsp_len = ALIGN(rsp_len, 4); + + return rtsx_usb_transfer_data(ucr, + usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), + ucr->rsp_buf, rsp_len, 0, NULL, timeout); +} +EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); + +static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) +{ + int ret; + + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); + ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); + if (ret) + return ret; + + ret = rtsx_usb_get_rsp(ucr, 2, 100); + if (ret) + return ret; + + *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | + ((ucr->rsp_buf[1] & 0x03) << 4); + + return 0; +} + +int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) +{ + int ret; + + if (!status) + return -EINVAL; + + if (polling_pipe == 0) + ret = usb_control_msg(ucr->pusb_dev, + usb_rcvctrlpipe(ucr->pusb_dev, 0), + RTSX_USB_REQ_POLL, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, status, 2, 100); + else + ret = rtsx_usb_get_status_with_bulk(ucr, status); + + /* usb_control_msg may return positive when success */ + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); + +static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) +{ + dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", + val, addr); + + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, + 0xFF, (addr >> 4) & 0x0F); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) +{ + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} +EXPORT_SYMBOL_GPL(rtsx_usb_write_register); + +int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) +{ + int ret; + + if (data != NULL) + *data = 0; + + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); + ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); + if (ret) + return ret; + + ret = rtsx_usb_get_rsp(ucr, 1, 100); + if (ret) + return ret; + + if (data != NULL) + *data = ucr->rsp_buf[0]; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_read_register); + +static inline u8 double_ssc_depth(u8 depth) +{ + return (depth > 1) ? (depth - 1) : depth; +} + +static u8 revise_ssc_depth(u8 ssc_depth, u8 div) +{ + if (div > CLK_DIV_1) { + if (ssc_depth > div - 1) + ssc_depth -= (div - 1); + else + ssc_depth = SSC_DEPTH_2M; + } + + return ssc_depth; +} + +int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ + int ret; + u8 n, clk_divider, mcu_cnt, div; + + if (!card_clock) { + ucr->cur_clk = 0; + return 0; + } + + if (initial_mode) { + /* We use 250k(around) here, in initial stage */ + clk_divider = SD_CLK_DIVIDE_128; + card_clock = 30000000; + } else { + clk_divider = SD_CLK_DIVIDE_0; + } + + ret = rtsx_usb_write_register(ucr, SD_CFG1, + SD_CLK_DIVIDE_MASK, clk_divider); + if (ret < 0) + return ret; + + card_clock /= 1000000; + dev_dbg(&ucr->pusb_intf->dev, + "Switch card clock to %dMHz\n", card_clock); + + if (!initial_mode && double_clk) + card_clock *= 2; + dev_dbg(&ucr->pusb_intf->dev, + "Internal SSC clock: %dMHz (cur_clk = %d)\n", + card_clock, ucr->cur_clk); + + if (card_clock == ucr->cur_clk) + return 0; + + /* Converting clock value into internal settings: n and div */ + n = card_clock - 2; + if ((card_clock <= 2) || (n > MAX_DIV_N)) + return -EINVAL; + + mcu_cnt = 60/card_clock + 3; + if (mcu_cnt > 15) + mcu_cnt = 15; + + /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ + + div = CLK_DIV_1; + while (n < MIN_DIV_N && div < CLK_DIV_4) { + n = (n + 2) * 2 - 2; + div++; + } + dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); + + if (double_clk) + ssc_depth = double_ssc_depth(ssc_depth); + + ssc_depth = revise_ssc_depth(ssc_depth, div); + dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); + + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, + 0x3F, (div << 4) | mcu_cnt); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, + SSC_DEPTH_MASK, ssc_depth); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); + if (vpclk) { + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, 0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + } + + ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); + if (ret < 0) + return ret; + + ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, + SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); + if (ret < 0) + return ret; + + /* Wait SSC clock stable */ + usleep_range(100, 1000); + + ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); + if (ret < 0) + return ret; + + ucr->cur_clk = card_clock; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); + +int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) +{ + int ret; + u16 val; + u16 cd_mask[] = { + [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), + [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) + }; + + ret = rtsx_usb_get_card_status(ucr, &val); + /* + * If get status fails, return 0 (ok) for the exclusive check + * and let the flow fail at somewhere else. + */ + if (ret) + return 0; + + if (val & cd_mask[card]) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); + +static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) +{ + int ret; + u8 val; + + rtsx_usb_init_cmd(ucr); + + if (CHECK_PKG(ucr, LQFP48)) { + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, + LDO3318_PWR_MASK, LDO_SUSPEND); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, + FORCE_LDO_POWERB, FORCE_LDO_POWERB); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, + 0x30, 0x10); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, + 0x03, 0x01); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, + 0x0C, 0x04); + } + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, + CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, + SD30_DRIVE_MASK, DRIVER_TYPE_D); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, + CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); + + if (ucr->is_rts5179) + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, + CARD_PULL_CTL5, 0x03, 0x01); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, + EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, + XD_INT | MS_INT | SD_INT, + XD_INT | MS_INT | SD_INT); + + ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); + if (ret) + return ret; + + /* config non-crystal mode */ + rtsx_usb_read_register(ucr, CFG_MODE, &val); + if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { + ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); + if (ret) + return ret; + } + + return 0; +} + +static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) +{ + int ret; + u8 val; + + rtsx_usb_clear_fsm_err(ucr); + + /* power on SSC */ + ret = rtsx_usb_write_register(ucr, + FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); + if (ret) + return ret; + + usleep_range(100, 1000); + ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); + if (ret) + return ret; + + /* determine IC version */ + ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); + if (ret) + return ret; + + ucr->ic_version = val & HW_VER_MASK; + + /* determine package */ + ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); + if (ret) + return ret; + + if (val & CARD_SHARE_LQFP_SEL) { + ucr->package = LQFP48; + dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); + } else { + ucr->package = QFN24; + dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); + } + + /* determine IC variations */ + rtsx_usb_read_register(ucr, CFG_MODE_1, &val); + if (val & RTS5179) { + ucr->is_rts5179 = true; + dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); + } else { + ucr->is_rts5179 = false; + } + + return rtsx_usb_reset_chip(ucr); +} + +static int rtsx_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct rtsx_ucr *ucr; + int ret; + + dev_dbg(&intf->dev, + ": Realtek USB Card Reader found at bus %03d address %03d\n", + usb_dev->bus->busnum, usb_dev->devnum); + + ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); + if (!ucr) + return -ENOMEM; + + ucr->pusb_dev = usb_dev; + + ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, + GFP_KERNEL, &ucr->iobuf_dma); + if (!ucr->iobuf) + return -ENOMEM; + + usb_set_intfdata(intf, ucr); + + ucr->vendor_id = id->idVendor; + ucr->product_id = id->idProduct; + ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; + + mutex_init(&ucr->dev_mutex); + + ucr->pusb_intf = intf; + + /* initialize */ + ret = rtsx_usb_init_chip(ucr); + if (ret) + goto out_init_fail; + + ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells, + ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL); + if (ret) + goto out_init_fail; + + /* initialize USB SG transfer timer */ + init_timer(&ucr->sg_timer); + setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); +#ifdef CONFIG_PM + intf->needs_remote_wakeup = 1; + usb_enable_autosuspend(usb_dev); +#endif + + return 0; + +out_init_fail: + usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, + ucr->iobuf_dma); + return ret; +} + +static void rtsx_usb_disconnect(struct usb_interface *intf) +{ + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + + dev_dbg(&intf->dev, "%s called\n", __func__); + + mfd_remove_devices(&intf->dev); + + usb_set_intfdata(ucr->pusb_intf, NULL); + usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, + ucr->iobuf_dma); +} + +#ifdef CONFIG_PM +static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct rtsx_ucr *ucr = + (struct rtsx_ucr *)usb_get_intfdata(intf); + + dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n", + __func__, message.event); + + mutex_lock(&ucr->dev_mutex); + rtsx_usb_turn_off_led(ucr); + mutex_unlock(&ucr->dev_mutex); + return 0; +} + +static int rtsx_usb_resume(struct usb_interface *intf) +{ + return 0; +} + +static int rtsx_usb_reset_resume(struct usb_interface *intf) +{ + struct rtsx_ucr *ucr = + (struct rtsx_ucr *)usb_get_intfdata(intf); + + rtsx_usb_reset_chip(ucr); + return 0; +} + +#else /* CONFIG_PM */ + +#define rtsx_usb_suspend NULL +#define rtsx_usb_resume NULL +#define rtsx_usb_reset_resume NULL + +#endif /* CONFIG_PM */ + + +static int rtsx_usb_pre_reset(struct usb_interface *intf) +{ + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + + mutex_lock(&ucr->dev_mutex); + return 0; +} + +static int rtsx_usb_post_reset(struct usb_interface *intf) +{ + struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); + + mutex_unlock(&ucr->dev_mutex); + return 0; +} + +static struct usb_device_id rtsx_usb_usb_ids[] = { + { USB_DEVICE(0x0BDA, 0x0129) }, + { USB_DEVICE(0x0BDA, 0x0139) }, + { USB_DEVICE(0x0BDA, 0x0140) }, + { } +}; + +static struct usb_driver rtsx_usb_driver = { + .name = "rtsx_usb", + .probe = rtsx_usb_probe, + .disconnect = rtsx_usb_disconnect, + .suspend = rtsx_usb_suspend, + .resume = rtsx_usb_resume, + .reset_resume = rtsx_usb_reset_resume, + .pre_reset = rtsx_usb_pre_reset, + .post_reset = rtsx_usb_post_reset, + .id_table = rtsx_usb_usb_ids, + .supports_autosuspend = 1, + .soft_unbind = 1, +}; + +module_usb_driver(rtsx_usb_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Roger Tseng "); +MODULE_DESCRIPTION("Realtek USB Card Reader Driver"); -- cgit v1.2.3 From 82ae61c4b0ccfd55a31b3afc267953d0a0d416ad Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 14 Feb 2014 15:21:05 +0530 Subject: mfd: wm8400-core: Remove unnecessary goto Return directly to avoid redundant lines of code. Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/wm8400-core.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index d66d256551fb..e5eae751aa1b 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -161,31 +161,19 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8400 *wm8400; - int ret; wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL); - if (wm8400 == NULL) { - ret = -ENOMEM; - goto err; - } + if (!wm8400) + return -ENOMEM; wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config); - if (IS_ERR(wm8400->regmap)) { - ret = PTR_ERR(wm8400->regmap); - goto err; - } + if (IS_ERR(wm8400->regmap)) + return PTR_ERR(wm8400->regmap); wm8400->dev = &i2c->dev; i2c_set_clientdata(i2c, wm8400); - ret = wm8400_init(wm8400, dev_get_platdata(&i2c->dev)); - if (ret != 0) - goto err; - - return 0; - -err: - return ret; + return wm8400_init(wm8400, dev_get_platdata(&i2c->dev)); } static int wm8400_i2c_remove(struct i2c_client *i2c) -- cgit v1.2.3 From c1d12c784c49980e4cbe57b7e6cc14b406449099 Mon Sep 17 00:00:00 2001 From: "Opensource [Steve Twiss]" Date: Fri, 14 Feb 2014 14:08:11 +0000 Subject: mfd: da9063: Add support for production silicon variant code Add the correct silicon variant code ID (0x5) to the driver. This new code is the 'production' variant code ID for DA9063. This patch will remove the older variant code ID which matches the pre-production silicon ID (0x3) for the DA9063 chip. There is also some small amount of correction done in this patch: it splits the revision code and correctly names it according to the hardware specification and moves the dev_info() call before the variant ID test. Signed-off-by: Opensource [Steve Twiss] Signed-off-by: Lee Jones --- drivers/mfd/da9063-core.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index 26937cd01071..e70ae315abc7 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -110,7 +110,7 @@ static const struct mfd_cell da9063_devs[] = { int da9063_device_init(struct da9063 *da9063, unsigned int irq) { struct da9063_pdata *pdata = da9063->dev->platform_data; - int model, revision; + int model, variant_id, variant_code; int ret; if (pdata) { @@ -141,23 +141,26 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) return -ENODEV; } - ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision); + ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id); if (ret < 0) { - dev_err(da9063->dev, "Cannot read chip revision id.\n"); + dev_err(da9063->dev, "Cannot read chip variant id.\n"); return -EIO; } - revision >>= DA9063_CHIP_VARIANT_SHIFT; - if (revision != 3) { - dev_err(da9063->dev, "Unknown chip revision: %d\n", revision); + + variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT; + + dev_info(da9063->dev, + "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n", + model, variant_id); + + if (variant_code != PMIC_DA9063_BB) { + dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n", + variant_code); return -ENODEV; } da9063->model = model; - da9063->revision = revision; - - dev_info(da9063->dev, - "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n", - model, revision); + da9063->variant_code = variant_code; ret = da9063_irq_init(da9063); if (ret) { -- cgit v1.2.3 From facd9939403cb5769190054a600474399e776e3a Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 14 Feb 2014 15:01:54 +0100 Subject: mfd: lpc_ich: Add support for Intel Avoton GPIOs Signed-off-by: Vincent Donnefort Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 010c1b490478..17eff0925ac1 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -499,6 +499,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { [LPC_AVN] = { .name = "Avoton SoC", .iTCO_version = 1, + .gpio_version = AVOTON_GPIO, }, [LPC_COLETO] = { .name = "Coleto Creek", -- cgit v1.2.3 From ba7f74fe2bbac3a3bcc709e60066d3768a55ca7f Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 14 Feb 2014 15:01:55 +0100 Subject: gpio: ich: Add blink capability option This patch allows gpio_ich driver to be aware of non blink capable chipsets. Acked-by: Linus Walleij Signed-off-by: Vincent Donnefort Signed-off-by: Lee Jones --- drivers/gpio/gpio-ich.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index f5bf3c38bca6..82887c53be38 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -62,6 +62,9 @@ struct ichx_desc { /* Max GPIO pins the chipset can have */ uint ngpio; + /* GPO_BLINK is available on this chipset */ + bool have_blink; + /* Whether the chipset has GPIO in GPE0_STS in the PM IO region */ bool uses_gpe0; @@ -151,7 +154,7 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { /* Disable blink hardware which is available for GPIOs from 0 to 31. */ - if (nr < 32) + if (nr < 32 && ichx_priv.desc->have_blink) ichx_write_bit(GPO_BLINK, nr, 0, 0); /* Set GPIO output value. */ @@ -266,6 +269,7 @@ static struct ichx_desc ich6_desc = { .uses_gpe0 = true, .ngpio = 50, + .have_blink = true, }; /* Intel 3100 */ @@ -290,19 +294,23 @@ static struct ichx_desc i3100_desc = { /* ICH7 and ICH8-based */ static struct ichx_desc ich7_desc = { .ngpio = 50, + .have_blink = true, }; /* ICH9-based */ static struct ichx_desc ich9_desc = { .ngpio = 61, + .have_blink = true, }; /* ICH10-based - Consumer/corporate versions have different amount of GPIO */ static struct ichx_desc ich10_cons_desc = { .ngpio = 61, + .have_blink = true, }; static struct ichx_desc ich10_corp_desc = { .ngpio = 72, + .have_blink = true, }; /* Intel 5 series, 6 series, 3400 series, and C200 series */ -- cgit v1.2.3 From bb62a35bd5d96e506af0ea8dd145480b9172a2a6 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 14 Feb 2014 15:01:56 +0100 Subject: gpio: ich: Add support for multiple register addresses This patch introduces regs and reglen pointers which allow a chipset to have register addresses differing from ICH ones. Acked-by: Linus Walleij Signed-off-by: Vincent Donnefort Signed-off-by: Lee Jones --- drivers/gpio/gpio-ich.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 82887c53be38..f3eb1c52f97b 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -62,6 +62,10 @@ struct ichx_desc { /* Max GPIO pins the chipset can have */ uint ngpio; + /* chipset registers */ + const u8 (*regs)[3]; + const u8 *reglen; + /* GPO_BLINK is available on this chipset */ bool have_blink; @@ -102,13 +106,16 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify) spin_lock_irqsave(&ichx_priv.lock, flags); - data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], + ichx_priv.gpio_base); if (val) data |= 1 << bit; else data &= ~(1 << bit); - ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base); - tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr], + ichx_priv.gpio_base); + tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], + ichx_priv.gpio_base); if (verify && data != tmp) ret = -EPERM; @@ -126,7 +133,8 @@ static int ichx_read_bit(int reg, unsigned nr) spin_lock_irqsave(&ichx_priv.lock, flags); - data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); + data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], + ichx_priv.gpio_base); spin_unlock_irqrestore(&ichx_priv.lock, flags); @@ -295,27 +303,37 @@ static struct ichx_desc i3100_desc = { static struct ichx_desc ich7_desc = { .ngpio = 50, .have_blink = true, + .regs = ichx_regs, + .reglen = ichx_reglen, }; /* ICH9-based */ static struct ichx_desc ich9_desc = { .ngpio = 61, .have_blink = true, + .regs = ichx_regs, + .reglen = ichx_reglen, }; /* ICH10-based - Consumer/corporate versions have different amount of GPIO */ static struct ichx_desc ich10_cons_desc = { .ngpio = 61, .have_blink = true, + .regs = ichx_regs, + .reglen = ichx_reglen, }; static struct ichx_desc ich10_corp_desc = { .ngpio = 72, .have_blink = true, + .regs = ichx_regs, + .reglen = ichx_reglen, }; /* Intel 5 series, 6 series, 3400 series, and C200 series */ static struct ichx_desc intel5_desc = { .ngpio = 76, + .regs = ichx_regs, + .reglen = ichx_reglen, }; static int ichx_gpio_request_regions(struct resource *res_base, @@ -326,11 +344,12 @@ static int ichx_gpio_request_regions(struct resource *res_base, if (!res_base || !res_base->start || !res_base->end) return -ENODEV; - for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { + for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) { if (!(use_gpio & (1 << i))) continue; - if (!request_region(res_base->start + ichx_regs[0][i], - ichx_reglen[i], name)) + if (!request_region( + res_base->start + ichx_priv.desc->regs[0][i], + ichx_priv.desc->reglen[i], name)) goto request_err; } return 0; @@ -340,8 +359,8 @@ request_err: for (i--; i >= 0; i--) { if (!(use_gpio & (1 << i))) continue; - release_region(res_base->start + ichx_regs[0][i], - ichx_reglen[i]); + release_region(res_base->start + ichx_priv.desc->regs[0][i], + ichx_priv.desc->reglen[i]); } return -EBUSY; } @@ -350,11 +369,11 @@ static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio) { int i; - for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { + for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) { if (!(use_gpio & (1 << i))) continue; - release_region(res_base->start + ichx_regs[0][i], - ichx_reglen[i]); + release_region(res_base->start + ichx_priv.desc->regs[0][i], + ichx_priv.desc->reglen[i]); } } -- cgit v1.2.3 From e6540f332447b2fe5c2cd8774890c80f29fe5c75 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 14 Feb 2014 15:01:57 +0100 Subject: gpio: ich: Add output levels cache support This patch allows GPIO driver to cache GPIO_LVL output registers. The aim is to support chipsets on which GPIO_LVL value can't be read for output pins. Caching output levels implies the first output values reading as 0. The driver so can't be aware of set values GPIOs by bootloader or BIOS. Acked-by: Linus Walleij Signed-off-by: Vincent Donnefort Signed-off-by: Lee Jones --- drivers/gpio/gpio-ich.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index f3eb1c52f97b..bfef20f8ab48 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -78,6 +78,12 @@ struct ichx_desc { /* Some chipsets have quirks, let these use their own request/get */ int (*request)(struct gpio_chip *chip, unsigned offset); int (*get)(struct gpio_chip *chip, unsigned offset); + + /* + * Some chipsets don't let reading output values on GPIO_LVL register + * this option allows driver caching written output values + */ + bool use_outlvl_cache; }; static struct { @@ -89,6 +95,7 @@ static struct { struct ichx_desc *desc; /* Pointer to chipset-specific description */ u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */ u8 use_gpio; /* Which GPIO groups are usable */ + int outlvl_cache[3]; /* cached output values */ } ichx_priv; static int modparam_gpiobase = -1; /* dynamic */ @@ -106,14 +113,21 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify) spin_lock_irqsave(&ichx_priv.lock, flags); - data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], - ichx_priv.gpio_base); + if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) + data = ichx_priv.outlvl_cache[reg_nr]; + else + data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], + ichx_priv.gpio_base); + if (val) data |= 1 << bit; else data &= ~(1 << bit); ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr], ichx_priv.gpio_base); + if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) + ichx_priv.outlvl_cache[reg_nr] = data; + tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], ichx_priv.gpio_base); if (verify && data != tmp) @@ -136,6 +150,9 @@ static int ichx_read_bit(int reg, unsigned nr) data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], ichx_priv.gpio_base); + if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) + data = ichx_priv.outlvl_cache[reg_nr] | data; + spin_unlock_irqrestore(&ichx_priv.lock, flags); return data & (1 << bit) ? 1 : 0; -- cgit v1.2.3 From 3b9231893e5731e2212645a92f1d3d0776c58e1a Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 14 Feb 2014 15:01:58 +0100 Subject: gpio: ich: Add support for Intel Avoton This patch adds support for Atom C2000 series (Avoton and Rangeley). And has the following options: - New addresses register. - Caching output levels (see Intel external design spec, table 48-29) - No hardware blink. Acked-by: Linus Walleij Signed-off-by: Vincent Donnefort Signed-off-by: Lee Jones --- drivers/gpio/gpio-ich.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index bfef20f8ab48..e73c6755a5eb 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -1,5 +1,5 @@ /* - * Intel ICH6-10, Series 5 and 6 GPIO driver + * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver * * Copyright (C) 2010 Extreme Engineering Solutions. * @@ -55,6 +55,16 @@ static const u8 ichx_reglen[3] = { 0x30, 0x10, 0x10, }; +static const u8 avoton_regs[4][3] = { + {0x00, 0x80, 0x00}, + {0x04, 0x84, 0x00}, + {0x08, 0x88, 0x00}, +}; + +static const u8 avoton_reglen[3] = { + 0x10, 0x10, 0x00, +}; + #define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) #define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start) @@ -353,6 +363,17 @@ static struct ichx_desc intel5_desc = { .reglen = ichx_reglen, }; +/* Avoton */ +static struct ichx_desc avoton_desc = { + /* Avoton has only 59 GPIOs, but we assume the first set of register + * (Core) has 32 instead of 31 to keep gpio-ich compliance + */ + .ngpio = 60, + .regs = avoton_regs, + .reglen = avoton_reglen, + .use_outlvl_cache = true, +}; + static int ichx_gpio_request_regions(struct resource *res_base, const char *name, u8 use_gpio) { @@ -427,6 +448,9 @@ static int ichx_gpio_probe(struct platform_device *pdev) case ICH_V10CONS_GPIO: ichx_priv.desc = &ich10_cons_desc; break; + case AVOTON_GPIO: + ichx_priv.desc = &avoton_desc; + break; default: return -ENODEV; } -- cgit v1.2.3 From a3ee75092e3722e3ca03060d6f5671906b69fad4 Mon Sep 17 00:00:00 2001 From: Michael Brunner Date: Mon, 24 Feb 2014 08:53:46 +0100 Subject: mfd: kempld: Add support for COMe-mBT10, COMe-cBT6 and COMe-cHL6 to Kontron PLD driver This patch adds DMI system IDs for the Kontron modules COMe-mBT10, COMe-cBT6 and COMe-cHL6 to the Kontron PLD driver. The list of supported products in the module description is also updated. Signed-off-by: Michael Brunner Acked-by: Christian Rauch Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 5 +++++ drivers/mfd/kempld-core.c | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 09fb5706208f..d93d1183991b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -270,13 +270,18 @@ config MFD_KEMPLD device may provide functions like watchdog, GPIO, UART and I2C bus. The following modules are supported: + * COMe-bHL6 * COMe-bIP# * COMe-bPC2 (ETXexpress-PC) * COMe-bSC# (ETXexpress-SC T#) + * COMe-cBT6 * COMe-cCT6 * COMe-cDC2 (microETXexpress-DC) + * COMe-cHL6 * COMe-cPC2 (microETXexpress-PC) + * COMe-mBT10 * COMe-mCT10 + * COMe-mTT10 (nanoETXexpress-TT) * ETX-OH This driver can also be built as a module. If so, the module diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index d3e23278d299..20a6afcfb36c 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -437,6 +437,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = { }, .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, + }, { + .ident = "CHL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, }, { .ident = "CHR2", .matches = { @@ -509,6 +517,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = { }, .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, + }, { + .ident = "CVV6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, }, { .ident = "FRI2", .matches = { @@ -532,6 +548,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = { }, .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, + }, { + .ident = "MVV1", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, }, { .ident = "NTC1", .matches = { -- cgit v1.2.3 From 38d8974e331519836c1ef44a7a53dd7b3ad6a4e7 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 22 Feb 2014 10:02:25 +0400 Subject: mfd: syscon: Move diagnostic messages to dev_dbg() This patch moves diagnostic messages used for debugging purposes to dev_dbg(). Signed-off-by: Alexander Shiyan Signed-off-by: Lee Jones --- drivers/mfd/syscon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 71841f9181bd..26200565d6ab 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -152,7 +152,7 @@ static int syscon_probe(struct platform_device *pdev) platform_set_drvdata(pdev, syscon); - dev_info(dev, "regmap %pR registered\n", res); + dev_dbg(dev, "regmap %pR registered\n", res); return 0; } -- cgit v1.2.3 From 6c049b2ac983154ac7d48c250da49a09007df949 Mon Sep 17 00:00:00 2001 From: "Opensource [Anthony Olech]" Date: Wed, 19 Feb 2014 16:32:47 +0000 Subject: mfd: da9052: Add new DA9053 BC chip variant Add support for a new BC variant of the DA9053 PMIC. There is one difference between it and the AA, BA and BB. This patch also corrects a typing mistake in one of the BA name strings that was incorrectly typed as "ab". Signed-off-by: Anthony Olech Signed-off-by: Lee Jones --- drivers/mfd/da9052-i2c.c | 5 ++++- drivers/mfd/da9052-spi.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index c319c4ef5d49..6da8ec8ff800 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -75,6 +75,7 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) DA9052_PARK_REGISTER, &val); break; + case DA9053_BC: default: /* * For other chips parking of I2C register @@ -114,6 +115,7 @@ static const struct i2c_device_id da9052_i2c_id[] = { {"da9053-aa", DA9053_AA}, {"da9053-ba", DA9053_BA}, {"da9053-bb", DA9053_BB}, + {"da9053-bc", DA9053_BC}, {} }; @@ -121,8 +123,9 @@ static const struct i2c_device_id da9052_i2c_id[] = { static const struct of_device_id dialog_dt_ids[] = { { .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] }, { .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] }, - { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] }, + { .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] }, { .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] }, + { .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] }, { /* sentinel */ } }; #endif diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 0680bcbc53de..17666b40b70c 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -71,6 +71,7 @@ static struct spi_device_id da9052_spi_id[] = { {"da9053-aa", DA9053_AA}, {"da9053-ba", DA9053_BA}, {"da9053-bb", DA9053_BB}, + {"da9053-bc", DA9053_BC}, {} }; -- cgit v1.2.3 From f8935e1cfcccd4904ee1bd1420aa781d43175479 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Tue, 25 Feb 2014 14:28:20 +0100 Subject: mfd: twl6040: Remove duplicate register write When probing, regmap_register_patch() will bypass the cache and perform i2c writes for the given patches. It is thus unnecessary to manually set the TWL6040_REG_ACCCTL register just before, as it will be done when registering the twl6040_patch. Acked-by: Peter Ujfalusi Signed-off-by: Florian Vaussard Signed-off-by: Lee Jones --- drivers/mfd/twl6040.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 75316fb33448..f15114f8c258 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -703,7 +703,6 @@ static int twl6040_probe(struct i2c_client *client, } /* dual-access registers controlled by I2C only */ - twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); regmap_register_patch(twl6040->regmap, twl6040_patch, ARRAY_SIZE(twl6040_patch)); -- cgit v1.2.3 From 89d68998308e16c49ce18b402d5f702d5a1767e5 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Tue, 25 Feb 2014 14:28:19 +0100 Subject: mfd: twl6040: Check for error when reading revision register We may have an error when reading the revision register, so check for the returned value. Acked-by: Peter Ujfalusi Signed-off-by: Florian Vaussard Signed-off-by: Lee Jones --- drivers/mfd/twl6040.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index f15114f8c258..6e88f25832fb 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -661,6 +661,11 @@ static int twl6040_probe(struct i2c_client *client, init_completion(&twl6040->ready); twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); + if (twl6040->rev < 0) { + dev_err(&client->dev, "Failed to read revision register: %d\n", + twl6040->rev); + goto gpio_err; + } /* ERRATA: Automatic power-up is not possible in ES1.0 */ if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) -- cgit v1.2.3 From bc866fc7a8c4322de40b694ffcfcdda50ab82f35 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:19 -0800 Subject: mfd: pm8xxx: Move pm8xxx-irq.c contents into only driver that uses it The pm8xxx-irq.c code is practically mandatory given that the pm8921-core driver will WARN about it missing and the Kconfig marks it as default y when a PM8xxx chips is enabled. The only reason the file was split out was because we planned to support other pm8xxx chips with different pm8xxx-core.c files. Now that we have DT on ARM this isn't necessary because we should be able to support all the ssbi based PM8xxx chips in one driver and one file with no data bloat. Let's move this code into the only driver that uses it right now (pm8921) so that it's always compiled when needed. In the future we can rename pm8921-core.c to something more generic. Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 10 -- drivers/mfd/Makefile | 1 - drivers/mfd/pm8921-core.c | 348 +++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/pm8xxx-irq.c | 371 ---------------------------------------------- 4 files changed, 348 insertions(+), 382 deletions(-) delete mode 100644 drivers/mfd/pm8xxx-irq.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d93d1183991b..001668d8895f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -492,16 +492,6 @@ config MFD_PM8921_CORE Say M here if you want to include support for PM8921 chip as a module. This will build a module called "pm8921-core". -config MFD_PM8XXX_IRQ - bool "Qualcomm PM8xxx IRQ features" - depends on MFD_PM8XXX - default y if MFD_PM8XXX - help - This is the IRQ driver for Qualcomm PM 8xxx PMIC chips. - - This is required to use certain other PM 8xxx features, such as GPIO - and MPP. - config MFD_RDC321X tristate "RDC R-321x southbridge" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 377bd519884c..89a7951503ca 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -151,7 +151,6 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o -obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 484fe66e6c88..50e0a9b69b9d 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -14,6 +14,8 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ #include +#include +#include #include #include #include @@ -22,15 +24,361 @@ #include #include #include +#include + +#define SSBI_REG_ADDR_IRQ_BASE 0x1BB + +#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0) +#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1) +#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2) +#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3) +#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4) +#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5) +#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6) +#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7) +#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8) + +#define PM_IRQF_LVL_SEL 0x01 /* level select */ +#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */ +#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */ +#define PM_IRQF_CLR 0x08 /* clear interrupt */ +#define PM_IRQF_BITS_MASK 0x70 +#define PM_IRQF_BITS_SHIFT 4 +#define PM_IRQF_WRITE 0x80 + +#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \ + PM_IRQF_MASK_RE) #define REG_HWREV 0x002 /* PMIC4 revision */ #define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ +struct pm_irq_chip { + struct device *dev; + spinlock_t pm_irq_lock; + unsigned int devirq; + unsigned int irq_base; + unsigned int num_irqs; + unsigned int num_blocks; + unsigned int num_masters; + u8 config[0]; +}; + struct pm8921 { struct device *dev; struct pm_irq_chip *irq_chip; }; +static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp) +{ + return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp); +} + +static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp) +{ + return pm8xxx_readb(chip->dev, + SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp); +} + +static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip) +{ + int rc; + + spin_lock(&chip->pm_irq_lock); + rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); + if (rc) { + pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); + goto bail; + } + + rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); + if (rc) + pr_err("Failed Reading Status rc=%d\n", rc); +bail: + spin_unlock(&chip->pm_irq_lock); + return rc; +} + +static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp) +{ + int rc; + + spin_lock(&chip->pm_irq_lock); + rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); + if (rc) { + pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); + goto bail; + } + + cp |= PM_IRQF_WRITE; + rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp); + if (rc) + pr_err("Failed Configuring IRQ rc=%d\n", rc); +bail: + spin_unlock(&chip->pm_irq_lock); + return rc; +} + +static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) +{ + int pmirq, irq, i, ret = 0; + u8 bits; + + ret = pm8xxx_read_block_irq(chip, block, &bits); + if (ret) { + pr_err("Failed reading %d block ret=%d", block, ret); + return ret; + } + if (!bits) { + pr_err("block bit set in master but no irqs: %d", block); + return 0; + } + + /* Check IRQ bits */ + for (i = 0; i < 8; i++) { + if (bits & (1 << i)) { + pmirq = block * 8 + i; + irq = pmirq + chip->irq_base; + generic_handle_irq(irq); + } + } + return 0; +} + +static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) +{ + u8 blockbits; + int block_number, i, ret = 0; + + ret = pm8xxx_read_master_irq(chip, master, &blockbits); + if (ret) { + pr_err("Failed to read master %d ret=%d\n", master, ret); + return ret; + } + if (!blockbits) { + pr_err("master bit set in root but no blocks: %d", master); + return 0; + } + + for (i = 0; i < 8; i++) + if (blockbits & (1 << i)) { + block_number = master * 8 + i; /* block # */ + ret |= pm8xxx_irq_block_handler(chip, block_number); + } + return ret; +} + +static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + u8 root; + int i, ret, masters = 0; + + ret = pm8xxx_read_root_irq(chip, &root); + if (ret) { + pr_err("Can't read root status ret=%d\n", ret); + return; + } + + /* on pm8xxx series masters start from bit 1 of the root */ + masters = root >> 1; + + /* Read allowed masters for blocks. */ + for (i = 0; i < chip->num_masters; i++) + if (masters & (1 << i)) + pm8xxx_irq_master_handler(chip, i); + + irq_chip->irq_ack(&desc->irq_data); +} + +static void pm8xxx_irq_mask_ack(struct irq_data *d) +{ + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int pmirq = d->irq - chip->irq_base; + int master, irq_bit; + u8 block, config; + + block = pmirq / 8; + master = block / 8; + irq_bit = pmirq % 8; + + config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; + pm8xxx_config_irq(chip, block, config); +} + +static void pm8xxx_irq_unmask(struct irq_data *d) +{ + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int pmirq = d->irq - chip->irq_base; + int master, irq_bit; + u8 block, config; + + block = pmirq / 8; + master = block / 8; + irq_bit = pmirq % 8; + + config = chip->config[pmirq]; + pm8xxx_config_irq(chip, block, config); +} + +static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int pmirq = d->irq - chip->irq_base; + int master, irq_bit; + u8 block, config; + + block = pmirq / 8; + master = block / 8; + irq_bit = pmirq % 8; + + chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) + | PM_IRQF_MASK_ALL; + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + if (flow_type & IRQF_TRIGGER_RISING) + chip->config[pmirq] &= ~PM_IRQF_MASK_RE; + if (flow_type & IRQF_TRIGGER_FALLING) + chip->config[pmirq] &= ~PM_IRQF_MASK_FE; + } else { + chip->config[pmirq] |= PM_IRQF_LVL_SEL; + + if (flow_type & IRQF_TRIGGER_HIGH) + chip->config[pmirq] &= ~PM_IRQF_MASK_RE; + else + chip->config[pmirq] &= ~PM_IRQF_MASK_FE; + } + + config = chip->config[pmirq] | PM_IRQF_CLR; + return pm8xxx_config_irq(chip, block, config); +} + +static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on) +{ + return 0; +} + +static struct irq_chip pm8xxx_irq_chip = { + .name = "pm8xxx", + .irq_mask_ack = pm8xxx_irq_mask_ack, + .irq_unmask = pm8xxx_irq_unmask, + .irq_set_type = pm8xxx_irq_set_type, + .irq_set_wake = pm8xxx_irq_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +/** + * pm8xxx_get_irq_stat - get the status of the irq line + * @chip: pointer to identify a pmic irq controller + * @irq: the irq number + * + * The pm8xxx gpio and mpp rely on the interrupt block to read + * the values on their pins. This function is to facilitate reading + * the status of a gpio or an mpp line. The caller has to convert the + * gpio number to irq number. + * + * RETURNS: + * an int indicating the value read on that line + */ +static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) +{ + int pmirq, rc; + u8 block, bits, bit; + unsigned long flags; + + if (chip == NULL || irq < chip->irq_base || + irq >= chip->irq_base + chip->num_irqs) + return -EINVAL; + + pmirq = irq - chip->irq_base; + + block = pmirq / 8; + bit = pmirq % 8; + + spin_lock_irqsave(&chip->pm_irq_lock, flags); + + rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block); + if (rc) { + pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n", + irq, pmirq, block, rc); + goto bail_out; + } + + rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); + if (rc) { + pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n", + irq, pmirq, block, rc); + goto bail_out; + } + + rc = (bits & (1 << bit)) ? 1 : 0; + +bail_out: + spin_unlock_irqrestore(&chip->pm_irq_lock, flags); + + return rc; +} + +static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev, + const struct pm8xxx_irq_platform_data *pdata) +{ + struct pm_irq_chip *chip; + int devirq, rc; + unsigned int pmirq; + + if (!pdata) { + pr_err("No platform data\n"); + return ERR_PTR(-EINVAL); + } + + devirq = pdata->devirq; + if (devirq < 0) { + pr_err("missing devirq\n"); + rc = devirq; + return ERR_PTR(-EINVAL); + } + + chip = kzalloc(sizeof(struct pm_irq_chip) + + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL); + if (!chip) { + pr_err("Cannot alloc pm_irq_chip struct\n"); + return ERR_PTR(-EINVAL); + } + + chip->dev = dev; + chip->devirq = devirq; + chip->irq_base = pdata->irq_base; + chip->num_irqs = pdata->irq_cdata.nirqs; + chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); + chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); + spin_lock_init(&chip->pm_irq_lock); + + for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) { + irq_set_chip_and_handler(chip->irq_base + pmirq, + &pm8xxx_irq_chip, + handle_level_irq); + irq_set_chip_data(chip->irq_base + pmirq, chip); +#ifdef CONFIG_ARM + set_irq_flags(chip->irq_base + pmirq, IRQF_VALID); +#else + irq_set_noprobe(chip->irq_base + pmirq); +#endif + } + + irq_set_irq_type(devirq, pdata->irq_trigger_flag); + irq_set_handler_data(devirq, chip); + irq_set_chained_handler(devirq, pm8xxx_irq_handler); + set_irq_wake(devirq, 1); + + return chip; +} + +static int pm8xxx_irq_exit(struct pm_irq_chip *chip) +{ + irq_set_chained_handler(chip->devirq, NULL); + kfree(chip); + return 0; +} + static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) { const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c deleted file mode 100644 index 1360e20adf11..000000000000 --- a/drivers/mfd/pm8xxx-irq.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) "%s: " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* PMIC8xxx IRQ */ - -#define SSBI_REG_ADDR_IRQ_BASE 0x1BB - -#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0) -#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1) -#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2) -#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3) -#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4) -#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5) -#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6) -#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7) -#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8) - -#define PM_IRQF_LVL_SEL 0x01 /* level select */ -#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */ -#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */ -#define PM_IRQF_CLR 0x08 /* clear interrupt */ -#define PM_IRQF_BITS_MASK 0x70 -#define PM_IRQF_BITS_SHIFT 4 -#define PM_IRQF_WRITE 0x80 - -#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \ - PM_IRQF_MASK_RE) - -struct pm_irq_chip { - struct device *dev; - spinlock_t pm_irq_lock; - unsigned int devirq; - unsigned int irq_base; - unsigned int num_irqs; - unsigned int num_blocks; - unsigned int num_masters; - u8 config[0]; -}; - -static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp) -{ - return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp); -} - -static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp) -{ - return pm8xxx_readb(chip->dev, - SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp); -} - -static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip) -{ - int rc; - - spin_lock(&chip->pm_irq_lock); - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); - if (rc) { - pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); - goto bail; - } - - rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); - if (rc) - pr_err("Failed Reading Status rc=%d\n", rc); -bail: - spin_unlock(&chip->pm_irq_lock); - return rc; -} - -static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp) -{ - int rc; - - spin_lock(&chip->pm_irq_lock); - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); - if (rc) { - pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); - goto bail; - } - - cp |= PM_IRQF_WRITE; - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp); - if (rc) - pr_err("Failed Configuring IRQ rc=%d\n", rc); -bail: - spin_unlock(&chip->pm_irq_lock); - return rc; -} - -static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) -{ - int pmirq, irq, i, ret = 0; - u8 bits; - - ret = pm8xxx_read_block_irq(chip, block, &bits); - if (ret) { - pr_err("Failed reading %d block ret=%d", block, ret); - return ret; - } - if (!bits) { - pr_err("block bit set in master but no irqs: %d", block); - return 0; - } - - /* Check IRQ bits */ - for (i = 0; i < 8; i++) { - if (bits & (1 << i)) { - pmirq = block * 8 + i; - irq = pmirq + chip->irq_base; - generic_handle_irq(irq); - } - } - return 0; -} - -static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) -{ - u8 blockbits; - int block_number, i, ret = 0; - - ret = pm8xxx_read_master_irq(chip, master, &blockbits); - if (ret) { - pr_err("Failed to read master %d ret=%d\n", master, ret); - return ret; - } - if (!blockbits) { - pr_err("master bit set in root but no blocks: %d", master); - return 0; - } - - for (i = 0; i < 8; i++) - if (blockbits & (1 << i)) { - block_number = master * 8 + i; /* block # */ - ret |= pm8xxx_irq_block_handler(chip, block_number); - } - return ret; -} - -static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); - struct irq_chip *irq_chip = irq_desc_get_chip(desc); - u8 root; - int i, ret, masters = 0; - - ret = pm8xxx_read_root_irq(chip, &root); - if (ret) { - pr_err("Can't read root status ret=%d\n", ret); - return; - } - - /* on pm8xxx series masters start from bit 1 of the root */ - masters = root >> 1; - - /* Read allowed masters for blocks. */ - for (i = 0; i < chip->num_masters; i++) - if (masters & (1 << i)) - pm8xxx_irq_master_handler(chip, i); - - irq_chip->irq_ack(&desc->irq_data); -} - -static void pm8xxx_irq_mask_ack(struct irq_data *d) -{ - struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; - u8 block, config; - - block = pmirq / 8; - master = block / 8; - irq_bit = pmirq % 8; - - config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; - pm8xxx_config_irq(chip, block, config); -} - -static void pm8xxx_irq_unmask(struct irq_data *d) -{ - struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; - u8 block, config; - - block = pmirq / 8; - master = block / 8; - irq_bit = pmirq % 8; - - config = chip->config[pmirq]; - pm8xxx_config_irq(chip, block, config); -} - -static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; - u8 block, config; - - block = pmirq / 8; - master = block / 8; - irq_bit = pmirq % 8; - - chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) - | PM_IRQF_MASK_ALL; - if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - if (flow_type & IRQF_TRIGGER_RISING) - chip->config[pmirq] &= ~PM_IRQF_MASK_RE; - if (flow_type & IRQF_TRIGGER_FALLING) - chip->config[pmirq] &= ~PM_IRQF_MASK_FE; - } else { - chip->config[pmirq] |= PM_IRQF_LVL_SEL; - - if (flow_type & IRQF_TRIGGER_HIGH) - chip->config[pmirq] &= ~PM_IRQF_MASK_RE; - else - chip->config[pmirq] &= ~PM_IRQF_MASK_FE; - } - - config = chip->config[pmirq] | PM_IRQF_CLR; - return pm8xxx_config_irq(chip, block, config); -} - -static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on) -{ - return 0; -} - -static struct irq_chip pm8xxx_irq_chip = { - .name = "pm8xxx", - .irq_mask_ack = pm8xxx_irq_mask_ack, - .irq_unmask = pm8xxx_irq_unmask, - .irq_set_type = pm8xxx_irq_set_type, - .irq_set_wake = pm8xxx_irq_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, -}; - -/** - * pm8xxx_get_irq_stat - get the status of the irq line - * @chip: pointer to identify a pmic irq controller - * @irq: the irq number - * - * The pm8xxx gpio and mpp rely on the interrupt block to read - * the values on their pins. This function is to facilitate reading - * the status of a gpio or an mpp line. The caller has to convert the - * gpio number to irq number. - * - * RETURNS: - * an int indicating the value read on that line - */ -int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) -{ - int pmirq, rc; - u8 block, bits, bit; - unsigned long flags; - - if (chip == NULL || irq < chip->irq_base || - irq >= chip->irq_base + chip->num_irqs) - return -EINVAL; - - pmirq = irq - chip->irq_base; - - block = pmirq / 8; - bit = pmirq % 8; - - spin_lock_irqsave(&chip->pm_irq_lock, flags); - - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block); - if (rc) { - pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n", - irq, pmirq, block, rc); - goto bail_out; - } - - rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); - if (rc) { - pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n", - irq, pmirq, block, rc); - goto bail_out; - } - - rc = (bits & (1 << bit)) ? 1 : 0; - -bail_out: - spin_unlock_irqrestore(&chip->pm_irq_lock, flags); - - return rc; -} -EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat); - -struct pm_irq_chip * pm8xxx_irq_init(struct device *dev, - const struct pm8xxx_irq_platform_data *pdata) -{ - struct pm_irq_chip *chip; - int devirq, rc; - unsigned int pmirq; - - if (!pdata) { - pr_err("No platform data\n"); - return ERR_PTR(-EINVAL); - } - - devirq = pdata->devirq; - if (devirq < 0) { - pr_err("missing devirq\n"); - rc = devirq; - return ERR_PTR(-EINVAL); - } - - chip = kzalloc(sizeof(struct pm_irq_chip) - + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL); - if (!chip) { - pr_err("Cannot alloc pm_irq_chip struct\n"); - return ERR_PTR(-EINVAL); - } - - chip->dev = dev; - chip->devirq = devirq; - chip->irq_base = pdata->irq_base; - chip->num_irqs = pdata->irq_cdata.nirqs; - chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); - chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); - spin_lock_init(&chip->pm_irq_lock); - - for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) { - irq_set_chip_and_handler(chip->irq_base + pmirq, - &pm8xxx_irq_chip, - handle_level_irq); - irq_set_chip_data(chip->irq_base + pmirq, chip); -#ifdef CONFIG_ARM - set_irq_flags(chip->irq_base + pmirq, IRQF_VALID); -#else - irq_set_noprobe(chip->irq_base + pmirq); -#endif - } - - irq_set_irq_type(devirq, pdata->irq_trigger_flag); - irq_set_handler_data(devirq, chip); - irq_set_chained_handler(devirq, pm8xxx_irq_handler); - set_irq_wake(devirq, 1); - - return chip; -} - -int pm8xxx_irq_exit(struct pm_irq_chip *chip) -{ - irq_set_chained_handler(chip->devirq, NULL); - kfree(chip); - return 0; -} -- cgit v1.2.3 From cced3548babc6d5338261f1b43ead62d93448567 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:20 -0800 Subject: mfd: pm8921: Update for genirq changes Since this code has been marked broken for some time a few genirq tree wide changes weren't made. set_irq_wake() was renamed to irq_set_irq_wake() in commit a0cd9ca2b (genirq: Namespace cleanup, 2011-02-10) and commit 10a8c383 (irq: introduce entry and exit functions for chained handlers) introduced the chained irq functions but this driver wasn't updated to use them. Fix these problems and remove the BROKEN marking on this driver. Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 - drivers/mfd/pm8921-core.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 001668d8895f..650c90f814ff 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -479,7 +479,6 @@ config MFD_PM8XXX config MFD_PM8921_CORE tristate "Qualcomm PM8921 PMIC chip" depends on (ARCH_MSM || HEXAGON) - depends on BROKEN select MFD_CORE select MFD_PM8XXX help diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 50e0a9b69b9d..9ddc31f7a71d 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -174,6 +175,8 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) u8 root; int i, ret, masters = 0; + chained_irq_enter(irq_chip, desc); + ret = pm8xxx_read_root_irq(chip, &root); if (ret) { pr_err("Can't read root status ret=%d\n", ret); @@ -188,7 +191,7 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) if (masters & (1 << i)) pm8xxx_irq_master_handler(chip, i); - irq_chip->irq_ack(&desc->irq_data); + chained_irq_exit(irq_chip, desc); } static void pm8xxx_irq_mask_ack(struct irq_data *d) @@ -367,7 +370,7 @@ static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev, irq_set_irq_type(devirq, pdata->irq_trigger_flag); irq_set_handler_data(devirq, chip); irq_set_chained_handler(devirq, pm8xxx_irq_handler); - set_irq_wake(devirq, 1); + irq_set_irq_wake(devirq, 1); return chip; } -- cgit v1.2.3 From dc1a95ccaa1158948bbc6648d6dadc534a30ed92 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:21 -0800 Subject: mfd: pm8921: Migrate to irqdomains Convert this driver to use irqdomains so that the PMIC's child devices can be converted to devicetree. Acked-by: Lee Jones Signed-off-by: Stephen Boyd --- drivers/mfd/Kconfig | 1 + drivers/mfd/pm8921-core.c | 198 ++++++++++++++++++---------------------------- 2 files changed, 76 insertions(+), 123 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 650c90f814ff..833d2c884437 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -479,6 +479,7 @@ config MFD_PM8XXX config MFD_PM8921_CORE tristate "Qualcomm PM8921 PMIC chip" depends on (ARCH_MSM || HEXAGON) + select IRQ_DOMAIN select MFD_CORE select MFD_PM8XXX help diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 9ddc31f7a71d..c25e7dae150b 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -17,15 +17,15 @@ #include #include #include +#include #include #include #include #include #include +#include #include -#include #include -#include #define SSBI_REG_ADDR_IRQ_BASE 0x1BB @@ -53,11 +53,12 @@ #define REG_HWREV 0x002 /* PMIC4 revision */ #define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ +#define PM8921_NR_IRQS 256 + struct pm_irq_chip { struct device *dev; spinlock_t pm_irq_lock; - unsigned int devirq; - unsigned int irq_base; + struct irq_domain *irqdomain; unsigned int num_irqs; unsigned int num_blocks; unsigned int num_masters; @@ -138,7 +139,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) for (i = 0; i < 8; i++) { if (bits & (1 << i)) { pmirq = block * 8 + i; - irq = pmirq + chip->irq_base; + irq = irq_find_mapping(chip->irqdomain, pmirq); generic_handle_irq(irq); } } @@ -197,12 +198,11 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) static void pm8xxx_irq_mask_ack(struct irq_data *d) { struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; + unsigned int pmirq = irqd_to_hwirq(d); + int irq_bit; u8 block, config; block = pmirq / 8; - master = block / 8; irq_bit = pmirq % 8; config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; @@ -212,12 +212,11 @@ static void pm8xxx_irq_mask_ack(struct irq_data *d) static void pm8xxx_irq_unmask(struct irq_data *d) { struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; + unsigned int pmirq = irqd_to_hwirq(d); + int irq_bit; u8 block, config; block = pmirq / 8; - master = block / 8; irq_bit = pmirq % 8; config = chip->config[pmirq]; @@ -227,12 +226,11 @@ static void pm8xxx_irq_unmask(struct irq_data *d) static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); - unsigned int pmirq = d->irq - chip->irq_base; - int master, irq_bit; + unsigned int pmirq = irqd_to_hwirq(d); + int irq_bit; u8 block, config; block = pmirq / 8; - master = block / 8; irq_bit = pmirq % 8; chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) @@ -287,12 +285,9 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) int pmirq, rc; u8 block, bits, bit; unsigned long flags; + struct irq_data *irq_data = irq_get_irq_data(irq); - if (chip == NULL || irq < chip->irq_base || - irq >= chip->irq_base + chip->num_irqs) - return -EINVAL; - - pmirq = irq - chip->irq_base; + pmirq = irq_data->hwirq; block = pmirq / 8; bit = pmirq % 8; @@ -321,67 +316,29 @@ bail_out: return rc; } -static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev, - const struct pm8xxx_irq_platform_data *pdata) -{ - struct pm_irq_chip *chip; - int devirq, rc; - unsigned int pmirq; - - if (!pdata) { - pr_err("No platform data\n"); - return ERR_PTR(-EINVAL); - } +static struct lock_class_key pm8xxx_irq_lock_class; - devirq = pdata->devirq; - if (devirq < 0) { - pr_err("missing devirq\n"); - rc = devirq; - return ERR_PTR(-EINVAL); - } - - chip = kzalloc(sizeof(struct pm_irq_chip) - + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL); - if (!chip) { - pr_err("Cannot alloc pm_irq_chip struct\n"); - return ERR_PTR(-EINVAL); - } - - chip->dev = dev; - chip->devirq = devirq; - chip->irq_base = pdata->irq_base; - chip->num_irqs = pdata->irq_cdata.nirqs; - chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); - chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); - spin_lock_init(&chip->pm_irq_lock); +static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct pm_irq_chip *chip = d->host_data; - for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) { - irq_set_chip_and_handler(chip->irq_base + pmirq, - &pm8xxx_irq_chip, - handle_level_irq); - irq_set_chip_data(chip->irq_base + pmirq, chip); + irq_set_lockdep_class(irq, &pm8xxx_irq_lock_class); + irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, chip); #ifdef CONFIG_ARM - set_irq_flags(chip->irq_base + pmirq, IRQF_VALID); + set_irq_flags(irq, IRQF_VALID); #else - irq_set_noprobe(chip->irq_base + pmirq); + irq_set_noprobe(irq); #endif - } - - irq_set_irq_type(devirq, pdata->irq_trigger_flag); - irq_set_handler_data(devirq, chip); - irq_set_chained_handler(devirq, pm8xxx_irq_handler); - irq_set_irq_wake(devirq, 1); - - return chip; -} - -static int pm8xxx_irq_exit(struct pm_irq_chip *chip) -{ - irq_set_chained_handler(chip->devirq, NULL); - kfree(chip); return 0; } +static const struct irq_domain_ops pm8xxx_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .map = pm8xxx_irq_domain_map, +}; + static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) { const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); @@ -432,42 +389,19 @@ static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_read_irq_stat = pm8921_read_irq_stat, }; -static int pm8921_add_subdevices(const struct pm8921_platform_data - *pdata, - struct pm8921 *pmic, - u32 rev) -{ - int ret = 0, irq_base = 0; - struct pm_irq_chip *irq_chip; - - if (pdata->irq_pdata) { - pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS; - pdata->irq_pdata->irq_cdata.rev = rev; - irq_base = pdata->irq_pdata->irq_base; - irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata); - - if (IS_ERR(irq_chip)) { - pr_err("Failed to init interrupts ret=%ld\n", - PTR_ERR(irq_chip)); - return PTR_ERR(irq_chip); - } - pmic->irq_chip = irq_chip; - } - return ret; -} - static int pm8921_probe(struct platform_device *pdev) { - const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev); struct pm8921 *pmic; int rc; u8 val; + unsigned int irq; u32 rev; + struct pm_irq_chip *chip; + unsigned int nirqs = PM8921_NR_IRQS; - if (!pdata) { - pr_err("missing platform data\n"); - return -EINVAL; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL); if (!pmic) { @@ -498,37 +432,55 @@ static int pm8921_probe(struct platform_device *pdev) pm8921_drvdata.pm_chip_data = pmic; platform_set_drvdata(pdev, &pm8921_drvdata); - rc = pm8921_add_subdevices(pdata, pmic, rev); + chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + + sizeof(chip->config[0]) * nirqs, + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + pmic->irq_chip = chip; + chip->dev = &pdev->dev; + chip->num_irqs = nirqs; + chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); + chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); + spin_lock_init(&chip->pm_irq_lock); + + chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs, + &pm8xxx_irq_domain_ops, + chip); + if (!chip->irqdomain) + return -ENODEV; + + irq_set_handler_data(irq, chip); + irq_set_chained_handler(irq, pm8xxx_irq_handler); + irq_set_irq_wake(irq, 1); + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (rc) { - pr_err("Cannot add subdevices rc=%d\n", rc); - goto err; + irq_set_chained_handler(irq, NULL); + irq_set_handler_data(irq, NULL); + irq_domain_remove(chip->irqdomain); } - /* gpio might not work if no irq device is found */ - WARN_ON(pmic->irq_chip == NULL); + return rc; +} +static int pm8921_remove_child(struct device *dev, void *unused) +{ + platform_device_unregister(to_platform_device(dev)); return 0; - -err: - mfd_remove_devices(pmic->dev); - return rc; } static int pm8921_remove(struct platform_device *pdev) { - struct pm8xxx_drvdata *drvdata; - struct pm8921 *pmic = NULL; - - drvdata = platform_get_drvdata(pdev); - if (drvdata) - pmic = drvdata->pm_chip_data; - if (pmic) { - mfd_remove_devices(pmic->dev); - if (pmic->irq_chip) { - pm8xxx_irq_exit(pmic->irq_chip); - pmic->irq_chip = NULL; - } - } + int irq = platform_get_irq(pdev, 0); + struct pm8921 *pmic = pm8921_drvdata.pm_chip_data; + struct pm_irq_chip *chip = pmic->irq_chip; + + device_for_each_child(&pdev->dev, NULL, pm8921_remove_child); + irq_set_chained_handler(irq, NULL); + irq_set_handler_data(irq, NULL); + irq_domain_remove(chip->irqdomain); return 0; } -- cgit v1.2.3 From e7b81fca7dd7cfa9f02722307e1a045f4565fec4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:23 -0800 Subject: mfd: pm8921: Use ssbi regmap Use a regmap so that the pm8xxx read/write APIs can be removed once all consumer drivers are converted. Reviewed-by: Mark Brown Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + drivers/mfd/pm8921-core.c | 66 +++++++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 833d2c884437..8c3b308cfda3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -482,6 +482,7 @@ config MFD_PM8921_CORE select IRQ_DOMAIN select MFD_CORE select MFD_PM8XXX + select REGMAP help If you say yes to this option, support will be included for the built-in PM8921 PMIC chip. diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index c25e7dae150b..e9340bd6d1ab 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ struct pm_irq_chip { struct device *dev; + struct regmap *regmap; spinlock_t pm_irq_lock; struct irq_domain *irqdomain; unsigned int num_irqs; @@ -70,29 +72,19 @@ struct pm8921 { struct pm_irq_chip *irq_chip; }; -static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp) -{ - return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp); -} - -static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp) -{ - return pm8xxx_readb(chip->dev, - SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp); -} - -static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip) +static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, + unsigned int *ip) { int rc; spin_lock(&chip->pm_irq_lock); - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); + rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); if (rc) { pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); goto bail; } - rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); + rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip); if (rc) pr_err("Failed Reading Status rc=%d\n", rc); bail: @@ -100,19 +92,20 @@ bail: return rc; } -static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp) +static int +pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp) { int rc; spin_lock(&chip->pm_irq_lock); - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); + rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); if (rc) { pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); goto bail; } cp |= PM_IRQF_WRITE; - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp); + rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp); if (rc) pr_err("Failed Configuring IRQ rc=%d\n", rc); bail: @@ -123,7 +116,7 @@ bail: static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) { int pmirq, irq, i, ret = 0; - u8 bits; + unsigned int bits; ret = pm8xxx_read_block_irq(chip, block, &bits); if (ret) { @@ -148,10 +141,11 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) { - u8 blockbits; + unsigned int blockbits; int block_number, i, ret = 0; - ret = pm8xxx_read_master_irq(chip, master, &blockbits); + ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master, + &blockbits); if (ret) { pr_err("Failed to read master %d ret=%d\n", master, ret); return ret; @@ -173,12 +167,12 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) { struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - u8 root; + unsigned int root; int i, ret, masters = 0; chained_irq_enter(irq_chip, desc); - ret = pm8xxx_read_root_irq(chip, &root); + ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root); if (ret) { pr_err("Can't read root status ret=%d\n", ret); return; @@ -283,7 +277,7 @@ static struct irq_chip pm8xxx_irq_chip = { static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) { int pmirq, rc; - u8 block, bits, bit; + unsigned int block, bits, bit; unsigned long flags; struct irq_data *irq_data = irq_get_irq_data(irq); @@ -294,14 +288,14 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) spin_lock_irqsave(&chip->pm_irq_lock, flags); - rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block); + rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block); if (rc) { pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n", irq, pmirq, block, rc); goto bail_out; } - rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); + rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits); if (rc) { pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n", irq, pmirq, block, rc); @@ -389,11 +383,21 @@ static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_read_irq_stat = pm8921_read_irq_stat, }; +static const struct regmap_config ssbi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x3ff, + .fast_io = true, + .reg_read = ssbi_reg_read, + .reg_write = ssbi_reg_write +}; + static int pm8921_probe(struct platform_device *pdev) { struct pm8921 *pmic; + struct regmap *regmap; int rc; - u8 val; + unsigned int val; unsigned int irq; u32 rev; struct pm_irq_chip *chip; @@ -409,8 +413,13 @@ static int pm8921_probe(struct platform_device *pdev) return -ENOMEM; } + regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent, + &ssbi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + /* Read PMIC chip revision */ - rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); + rc = regmap_read(regmap, REG_HWREV, &val); if (rc) { pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc); return rc; @@ -419,7 +428,7 @@ static int pm8921_probe(struct platform_device *pdev) rev = val; /* Read PMIC chip revision 2 */ - rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val)); + rc = regmap_read(regmap, REG_HWREV_2, &val); if (rc) { pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", REG_HWREV_2, rc); @@ -440,6 +449,7 @@ static int pm8921_probe(struct platform_device *pdev) pmic->irq_chip = chip; chip->dev = &pdev->dev; + chip->regmap = regmap; chip->num_irqs = nirqs; chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); -- cgit v1.2.3 From c5865a5315dcf13fcad50aa8630eed11c9ff95a9 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:24 -0800 Subject: mfd: pm8921: Add DT match table Allow this driver to probe based on devicetree. Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/pm8921-core.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index e9340bd6d1ab..3aab6ace5eb5 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -392,6 +392,13 @@ static const struct regmap_config ssbi_regmap_config = { .reg_write = ssbi_reg_write }; +static const struct of_device_id pm8921_id_table[] = { + { .compatible = "qcom,pm8058", }, + { .compatible = "qcom,pm8921", }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8921_id_table); + static int pm8921_probe(struct platform_device *pdev) { struct pm8921 *pmic; @@ -501,6 +508,7 @@ static struct platform_driver pm8921_driver = { .driver = { .name = "pm8921-core", .owner = THIS_MODULE, + .of_match_table = pm8921_id_table, }, }; -- cgit v1.2.3 From 549f8db793494cd7fd040043037a1f0ad57d6959 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Feb 2014 10:59:25 -0800 Subject: mfd: pm8921: Loosen Kconfig dependency Allow this driver to be compiled on all ARM builds as it doesn't rely on anything within arm/mach-msm. Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8c3b308cfda3..83744359fb2d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -478,7 +478,7 @@ config MFD_PM8XXX config MFD_PM8921_CORE tristate "Qualcomm PM8921 PMIC chip" - depends on (ARCH_MSM || HEXAGON) + depends on (ARM || HEXAGON) select IRQ_DOMAIN select MFD_CORE select MFD_PM8XXX -- cgit v1.2.3 From 507c133b7dad837e6d0badbe9dc85310ab27a48a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 27 Feb 2014 20:39:18 +0900 Subject: mfd: ucb1x00-core: Use SIMPLE_DEV_PM_OPS macro Use SIMPLE_DEV_PM_OPS macro in order to make the code simpler. Signed-off-by: Jingoo Han Signed-off-by: Lee Jones --- drivers/mfd/ucb1x00-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 0313f839e8fa..153d595afaac 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -742,9 +742,7 @@ static int ucb1x00_resume(struct device *dev) } #endif -static const struct dev_pm_ops ucb1x00_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume) -}; +static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume); static struct mcp_driver ucb1x00_driver = { .drv = { -- cgit v1.2.3 From 471212d943f839c94aefd416feaeca6e26a6e1f6 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 21 Feb 2014 17:58:09 +0100 Subject: mfd: timberdale: Use pci_enable_msix_exact() instead of pci_enable_msix() As result of deprecation of MSI-X/MSI enablement functions pci_enable_msix() and pci_enable_msi_block() all drivers using these two interfaces need to be updated to use the new pci_enable_msi_range() or pci_enable_msi_exact() and pci_enable_msix_range() or pci_enable_msix_exact() interfaces. Signed-off-by: Alexander Gordeev Signed-off-by: Lee Jones --- drivers/mfd/timberdale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 2bc5cfb85204..6ce36d6970a4 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -715,7 +715,7 @@ static int timb_probe(struct pci_dev *dev, for (i = 0; i < TIMBERDALE_NR_IRQS; i++) msix_entries[i].entry = i; - err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS); + err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS); if (err) { dev_err(&dev->dev, "MSI-X init failed: %d, expected entries: %d\n", -- cgit v1.2.3 From 0cfe5c90c45c53f9d28d166d2b13bb54852742ba Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 2 Mar 2014 11:44:33 +0400 Subject: mfd: mc13xxx: Limit maximum SPI speed The patch adds the maximum speed limit in accordance with the PMIC datasheet if other value is not given in the devicetree description or board data. Signed-off-by: Alexander Shiyan Signed-off-by: Lee Jones --- drivers/mfd/mc13xxx-spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 38ab67829791..ee81a67207de 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -140,6 +140,8 @@ static int mc13xxx_spi_probe(struct spi_device *spi) mc13xxx->irq = spi->irq; + spi->max_speed_hz = spi->max_speed_hz ? : 26000000; + mc13xxx->regmap = devm_regmap_init(&spi->dev, ®map_mc13xxx_bus, &spi->dev, &mc13xxx_regmap_spi_config); -- cgit v1.2.3 From 6a5926e6975c9bca2ef6ff68cbd1cc17afc0e7d8 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 2 Mar 2014 11:44:34 +0400 Subject: mfd: mc13xxx: Add missing spi_setup() The probe routine should call spi_setup() to configure the SPI bus so it can properly communicate with the device. Signed-off-by: Alexander Shiyan Signed-off-by: Lee Jones --- drivers/mfd/mc13xxx-spi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index ee81a67207de..702925e242c9 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -141,6 +141,9 @@ static int mc13xxx_spi_probe(struct spi_device *spi) mc13xxx->irq = spi->irq; spi->max_speed_hz = spi->max_speed_hz ? : 26000000; + ret = spi_setup(spi); + if (ret) + return ret; mc13xxx->regmap = devm_regmap_init(&spi->dev, ®map_mc13xxx_bus, &spi->dev, -- cgit v1.2.3 From 2802c94c3981ce3024ff8b3526b8b2ff26351923 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Mar 2014 10:48:51 -0800 Subject: mfd: pm8921: Drop irq_set_lockdep_class() code This isn't necessary as we aren't setting the summary interrupt to wake up the system in the irq_wake() callback. Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/pm8921-core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 3aab6ace5eb5..4d486e91b602 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -310,14 +310,11 @@ bail_out: return rc; } -static struct lock_class_key pm8xxx_irq_lock_class; - static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct pm_irq_chip *chip = d->host_data; - irq_set_lockdep_class(irq, &pm8xxx_irq_lock_class); irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); irq_set_chip_data(irq, chip); #ifdef CONFIG_ARM -- cgit v1.2.3 From d2d24ad1c415c68a2d96dedae74421228025c899 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Mar 2014 10:48:52 -0800 Subject: mfd: pm8921: Use IRQCHIP_SKIP_SET_WAKE We don't need to implement a dummy irq_set_wake op if we just set IRQCHIP_SKIP_SET_WAKE. Suggested-by: Josh Cartwright Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/pm8921-core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 4d486e91b602..1153e8a3a556 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -247,18 +247,12 @@ static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) return pm8xxx_config_irq(chip, block, config); } -static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on) -{ - return 0; -} - static struct irq_chip pm8xxx_irq_chip = { .name = "pm8xxx", .irq_mask_ack = pm8xxx_irq_mask_ack, .irq_unmask = pm8xxx_irq_unmask, .irq_set_type = pm8xxx_irq_set_type, - .irq_set_wake = pm8xxx_irq_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; /** -- cgit v1.2.3 From 09507305a5b372812929f3e8acf9b72f35a88c1c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 4 Mar 2014 09:50:32 -0700 Subject: mfd: as3722: Make FUSE7_REG readable The FUSE7_REG register is not currently marked readable. This causes as3722_sd0_is_low_voltage() to emit an error during boot, and assume the range of the SD0 regulator: as3722-regulator as3722-regulator: Reg 0xa7 read failed: -5 Fixes: d4807ad2c4c0 ("regmap: Check readable regs in _regmap_read") [exposed the bug, by checking for readability] Fixes: 762a8ee80897 ("regulator: as3722: detect SD0 low-voltage mode") [left out this register from the readable list] Signed-off-by: Stephen Warren Signed-off-by: Lee Jones --- drivers/mfd/as3722.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c index c71ff0af1547..39fa554f13bb 100644 --- a/drivers/mfd/as3722.c +++ b/drivers/mfd/as3722.c @@ -277,6 +277,7 @@ static const struct regmap_range as3722_readable_ranges[] = { regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG), regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG), regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG), + regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG), }; static const struct regmap_access_table as3722_readable_table = { -- cgit v1.2.3 From 392966937d0da3be9fa679b74bd68ae0fc74d91d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 4 Mar 2014 15:11:47 +0530 Subject: mfd: vexpress: Staticize vexpress_config_bridges vexpress_config_bridges is local to this file. Signed-off-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/vexpress-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c index 0c9c3500e25d..d0db89d13e01 100644 --- a/drivers/mfd/vexpress-config.c +++ b/drivers/mfd/vexpress-config.c @@ -26,7 +26,7 @@ #define VEXPRESS_CONFIG_MAX_BRIDGES 2 -struct vexpress_config_bridge { +static struct vexpress_config_bridge { struct device_node *node; struct vexpress_config_bridge_info *info; struct list_head transactions; -- cgit v1.2.3 From d9a335155c5840796e2a49a9009cc2c8bdb224b2 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 5 Mar 2014 14:18:35 +0000 Subject: mfd: da9055: Add DT support for PMIC Signed-off-by: Adam Thomson Signed-off-by: Lee Jones --- drivers/mfd/da9055-i2c.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c index 8103e4362132..d4d4c165eb95 100644 --- a/drivers/mfd/da9055-i2c.c +++ b/drivers/mfd/da9055-i2c.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include @@ -66,6 +68,11 @@ static struct i2c_device_id da9055_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); +static const struct of_device_id da9055_of_match[] = { + { .compatible = "dlg,da9055-pmic", }, + { } +}; + static struct i2c_driver da9055_i2c_driver = { .probe = da9055_i2c_probe, .remove = da9055_i2c_remove, @@ -73,6 +80,7 @@ static struct i2c_driver da9055_i2c_driver = { .driver = { .name = "da9055-pmic", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(da9055_of_match), }, }; -- cgit v1.2.3 From 202f7680af1563516a057f884cea30be94d3014f Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Wed, 5 Mar 2014 13:34:25 -0600 Subject: mfd: pm8921: Fixup probe() error path when irq invalid platform_get_irq() returns a negative error code when an IRQ is invalid or unspecified. Make 'irq' signed to properly handle this. Reviewed-by: Stephen Boyd Signed-off-by: Josh Cartwright Signed-off-by: Lee Jones --- drivers/mfd/pm8921-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 1153e8a3a556..b97a97187ae9 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -394,9 +394,8 @@ static int pm8921_probe(struct platform_device *pdev) { struct pm8921 *pmic; struct regmap *regmap; - int rc; + int irq, rc; unsigned int val; - unsigned int irq; u32 rev; struct pm_irq_chip *chip; unsigned int nirqs = PM8921_NR_IRQS; -- cgit v1.2.3 From 416dc642afab52337d49d67170370b22b7057ab6 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 10 Mar 2014 14:57:33 +0400 Subject: mfd: syscon: Simplify syscon_match_pdevname() Since we do not want to add new platform IDs for the syscon driver, there is no reason to iterate over IDs. This patch simplifies syscon_match_pdevname() function to remove such iteration. Signed-off-by: Alexander Shiyan Signed-off-by: Lee Jones --- drivers/mfd/syscon.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 26200565d6ab..dbea55de4397 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -69,13 +69,6 @@ EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); static int syscon_match_pdevname(struct device *dev, void *data) { - struct platform_device *pdev = to_platform_device(dev); - const struct platform_device_id *id = platform_get_device_id(pdev); - - if (id) - if (!strcmp(id->name, (const char *)data)) - return 1; - return !strcmp(dev_name(dev), (const char *)data); } -- cgit v1.2.3 From 2d5d366a08f428aa2b26fca4478c510c89bc41f2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 10 Mar 2014 15:23:25 +0000 Subject: mfd: tps65218: Fix reported randconfig error Reported error was: ERROR: "regmap_del_irq_chip" [drivers/mfd/tps65218.ko] undefined! when CONFIG_REGMAP_IRQ is not enabled. Reported-by: Randy Dunlap Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 83744359fb2d..7838a5d308a8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -864,6 +864,7 @@ config MFD_TPS65218 depends on I2C select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help If you say yes here you get support for the TPS65218 series of Power Management chips. -- cgit v1.2.3 From 56816b700c8c773270f3aaf4c92be53e359a03fd Mon Sep 17 00:00:00 2001 From: Tomas Novotny Date: Mon, 10 Mar 2014 19:12:50 +0100 Subject: mfd: twl-core: Fix accessibility of some twl4030 audio registers There are some unused registers in twl4030 at I2C address 0x49 and function twl4030_49_nop_reg() is used to check accessibility of that registers. These registers are written in decimal format but the values are correct in hexadecimal format. (It can be checked few lines above the patched code - these registers are marked as unused there.) As a consequence three registers of audio submodule are treated as inaccessible (preamplifier carkit right and both handsfree registers). Cc: stable@vger.kernel.org Signed-off-by: Tomas Novotny Signed-off-by: Lee Jones --- drivers/mfd/twl-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index ed718328eff1..e87140bef667 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -282,11 +282,11 @@ static struct reg_default twl4030_49_defaults[] = { static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg) { switch (reg) { - case 0: - case 3: - case 40: - case 41: - case 42: + case 0x00: + case 0x03: + case 0x40: + case 0x41: + case 0x42: return false; default: return true; -- cgit v1.2.3 From 0c8a9dea215cd66c2d3d0f5ba96e4ff2c13ac929 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Mar 2014 15:21:42 +0100 Subject: mfd: max14577: Select REGMAP_IRQ MAXIM 14577 MFD driver uses the regmap_irq_chip so it's Kconfig entry should select REGMAP_IRQ. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7838a5d308a8..d22ca0fc873f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -327,6 +327,7 @@ config MFD_MAX14577 depends on I2C=y select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ select IRQ_DOMAIN help Say yes here to add support for Maxim Semiconductor MAX14577. -- cgit v1.2.3 From 037b60f2ca9403d02cbee60ad0a10f3806f9dc4b Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 11 Mar 2014 19:33:54 -0400 Subject: mfd: Add bcm590xx pmu driver Add a driver for the BCM590xx PMU multi-function devices. The driver initially supports regmap initialization and instantiation of the voltage regulator device function of the PMU. Signed-off-by: Matt Porter Reviewed-by: Tim Kryger Reviewed-by: Markus Mayer Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 8 +++++ drivers/mfd/Makefile | 1 + drivers/mfd/bcm590xx.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 drivers/mfd/bcm590xx.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d22ca0fc873f..7587c9e1a519 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -59,6 +59,14 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_BCM590XX + tristate "Broadcom BCM590xx PMUs" + select MFD_CORE + select REGMAP_I2C + depends on I2C + help + Support for the BCM590xx PMUs from Broadcom + config MFD_CROS_EC tristate "ChromeOS Embedded Controller" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 89a7951503ca..23835dfa615c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o +obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c new file mode 100644 index 000000000000..926a57e4d533 --- /dev/null +++ b/drivers/mfd/bcm590xx.c @@ -0,0 +1,93 @@ +/* + * Broadcom BCM590xx PMU + * + * Copyright 2014 Linaro Limited + * Author: Matt Porter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell bcm590xx_devs[] = { + { + .name = "bcm590xx-vregs", + }, +}; + +static const struct regmap_config bcm590xx_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BCM590XX_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + +static int bcm590xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct bcm590xx *bcm590xx; + int ret; + + bcm590xx = devm_kzalloc(&i2c->dev, sizeof(*bcm590xx), GFP_KERNEL); + if (!bcm590xx) + return -ENOMEM; + + i2c_set_clientdata(i2c, bcm590xx); + bcm590xx->dev = &i2c->dev; + bcm590xx->i2c_client = i2c; + + bcm590xx->regmap = devm_regmap_init_i2c(i2c, &bcm590xx_regmap_config); + if (IS_ERR(bcm590xx->regmap)) { + ret = PTR_ERR(bcm590xx->regmap); + dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + ret = mfd_add_devices(&i2c->dev, -1, bcm590xx_devs, + ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL); + if (ret < 0) + dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret); + + return ret; +} + +static const struct of_device_id bcm590xx_of_match[] = { + { .compatible = "brcm,bcm59056" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bcm590xx_of_match); + +static const struct i2c_device_id bcm590xx_i2c_id[] = { + { "bcm59056" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id); + +static struct i2c_driver bcm590xx_i2c_driver = { + .driver = { + .name = "bcm590xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(bcm590xx_of_match), + }, + .probe = bcm590xx_i2c_probe, + .id_table = bcm590xx_i2c_id, +}; +module_i2c_driver(bcm590xx_i2c_driver); + +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("BCM590xx multi-function driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bcm590xx"); -- cgit v1.2.3 From f5dccb15877b82a40950c6f752d5345c86189fc9 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:51 -0500 Subject: mfd: lpc_ich: Fix ACPI enable bitmask The original bitmask of 0x10 was incorrect and would result in a write to a reserved read-only bit instead of enabling the ACPI I/O region. Update it to the proper value of 0x80. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 17eff0925ac1..2be85a4b36c0 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -759,7 +759,7 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev) u8 reg_save; pci_read_config_byte(dev, priv->acpi.ctrl, ®_save); - pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10); + pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x80); priv->acpi.save = reg_save; } -- cgit v1.2.3 From f0776b8ce03ceb638c51b62f324844c71c446600 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:52 -0500 Subject: mfd: lpc_ich: Only configure watchdog or GPIO when present Some chipsets don't currently have GPIO support enabled. For these chipsets don't go through the process of initializing the GPIO region. Make the same change for the watchdog initialization for chipsets which may not enable the WDT in the future. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 2be85a4b36c0..cffa8367ddcf 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -967,13 +967,17 @@ static int lpc_ich_probe(struct pci_dev *dev, pci_set_drvdata(dev, priv); - ret = lpc_ich_init_wdt(dev); - if (!ret) - cell_added = true; + if (lpc_chipset_info[priv->chipset].iTCO_version) { + ret = lpc_ich_init_wdt(dev); + if (!ret) + cell_added = true; + } - ret = lpc_ich_init_gpio(dev); - if (!ret) - cell_added = true; + if (lpc_chipset_info[priv->chipset].gpio_version) { + ret = lpc_ich_init_gpio(dev); + if (!ret) + cell_added = true; + } /* * We only care if at least one or none of the cells registered -- cgit v1.2.3 From 429b941abd503c8936e116c819362323aafdbd50 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:53 -0500 Subject: mfd: lpc_ich: Remove lpc_ich_cfg struct use Future chipsets will use different register layouts that don't map cleanly to the lpc_ich_cfg fields. Remove the lpc_ich_cfg struct and add explicit fields to the higher level lpc_ich_priv structure. This change should have no functional impact. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 63 ++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index cffa8367ddcf..b24bae2bcdea 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -89,16 +89,16 @@ #define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i) #define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) -struct lpc_ich_cfg { - int base; - int ctrl; - int save; -}; - struct lpc_ich_priv { int chipset; - struct lpc_ich_cfg acpi; - struct lpc_ich_cfg gpio; + + int abase; /* ACPI base */ + int actrl; /* ACPI control or PMC base */ + int gbase; /* GPIO base */ + int gctrl; /* GPIO control */ + + int actrl_save; /* Cached ACPI control base value */ + int gctrl_save; /* Cached GPIO control value */ }; static struct resource wdt_ich_res[] = { @@ -742,14 +742,14 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev) { struct lpc_ich_priv *priv = pci_get_drvdata(dev); - if (priv->acpi.save >= 0) { - pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save); - priv->acpi.save = -1; + if (priv->actrl_save >= 0) { + pci_write_config_byte(dev, priv->actrl, priv->actrl_save); + priv->actrl_save = -1; } - if (priv->gpio.save >= 0) { - pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save); - priv->gpio.save = -1; + if (priv->gctrl_save >= 0) { + pci_write_config_byte(dev, priv->gctrl, priv->gctrl_save); + priv->gctrl_save = -1; } } @@ -758,9 +758,9 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev) struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, priv->acpi.ctrl, ®_save); - pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x80); - priv->acpi.save = reg_save; + pci_read_config_byte(dev, priv->actrl, ®_save); + pci_write_config_byte(dev, priv->actrl, reg_save | 0x80); + priv->actrl_save = reg_save; } static void lpc_ich_enable_gpio_space(struct pci_dev *dev) @@ -768,9 +768,9 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev) struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, priv->gpio.ctrl, ®_save); - pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10); - priv->gpio.save = reg_save; + pci_read_config_byte(dev, priv->gctrl, ®_save); + pci_write_config_byte(dev, priv->gctrl, reg_save | 0x10); + priv->gctrl_save = reg_save; } static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell) @@ -815,7 +815,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev) struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); + pci_read_config_dword(dev, priv->abase, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -841,7 +841,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev) gpe0_done: /* Setup GPIO base register */ - pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg); + pci_read_config_dword(dev, priv->gbase, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); @@ -891,7 +891,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); + pci_read_config_dword(dev, priv->abase, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -952,17 +952,18 @@ static int lpc_ich_probe(struct pci_dev *dev, return -ENOMEM; priv->chipset = id->driver_data; - priv->acpi.save = -1; - priv->acpi.base = ACPIBASE; - priv->acpi.ctrl = ACPICTRL; - priv->gpio.save = -1; + priv->actrl_save = -1; + priv->abase = ACPIBASE; + priv->actrl = ACPICTRL; + + priv->gctrl_save = -1; if (priv->chipset <= LPC_ICH5) { - priv->gpio.base = GPIOBASE_ICH0; - priv->gpio.ctrl = GPIOCTRL_ICH0; + priv->gbase = GPIOBASE_ICH0; + priv->gctrl = GPIOCTRL_ICH0; } else { - priv->gpio.base = GPIOBASE_ICH6; - priv->gpio.ctrl = GPIOCTRL_ICH6; + priv->gbase = GPIOBASE_ICH6; + priv->gctrl = GPIOCTRL_ICH6; } pci_set_drvdata(dev, priv); -- cgit v1.2.3 From eb71d4dec4a5e010e34b9d7afdb5af41884c388e Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:54 -0500 Subject: mfd: lpc_ich: Add support for iTCO v3 Some newer Atom CPUs, eg Avoton and Bay Trail, use slightly different register layouts for the iTCO than the current v1 and v2 iTCO. Differences from previous iTCO versions include: - The ACPI space is enabled in the "ACPI base address" register instead of the "ACPI control register" - The "no reboot" functionality is set in the "Power Management Configuration" register instead of the "General Control and Status" (GCS) register or PCI configuration space. - The "ACPI Control Register" is not present on v3. The "Power Management Configuration Base Address" register resides at the same address is Avoton/Bay Trail. To differentiate these newer chipsets create a new v3 iTCO version and update the MFD driver to support them. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 81 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index b24bae2bcdea..c0683370abbf 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -71,9 +71,11 @@ #define ACPIBASE_GPE_END 0x2f #define ACPIBASE_SMI_OFF 0x30 #define ACPIBASE_SMI_END 0x33 +#define ACPIBASE_PMC_OFF 0x08 +#define ACPIBASE_PMC_END 0x0c #define ACPIBASE_TCO_OFF 0x60 #define ACPIBASE_TCO_END 0x7f -#define ACPICTRL 0x44 +#define ACPICTRL_PMCBASE 0x44 #define ACPIBASE_GCS_OFF 0x3410 #define ACPIBASE_GCS_END 0x3414 @@ -93,11 +95,12 @@ struct lpc_ich_priv { int chipset; int abase; /* ACPI base */ - int actrl; /* ACPI control or PMC base */ + int actrl_pbase; /* ACPI control or PMC base */ int gbase; /* GPIO base */ int gctrl; /* GPIO control */ - int actrl_save; /* Cached ACPI control base value */ + int abase_save; /* Cached ACPI base value */ + int actrl_pbase_save; /* Cached ACPI control or PMC base value */ int gctrl_save; /* Cached GPIO control value */ }; @@ -110,7 +113,7 @@ static struct resource wdt_ich_res[] = { { .flags = IORESOURCE_IO, }, - /* GCS */ + /* GCS or PMC */ { .flags = IORESOURCE_MEM, }, @@ -742,9 +745,15 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev) { struct lpc_ich_priv *priv = pci_get_drvdata(dev); - if (priv->actrl_save >= 0) { - pci_write_config_byte(dev, priv->actrl, priv->actrl_save); - priv->actrl_save = -1; + if (priv->abase_save >= 0) { + pci_write_config_byte(dev, priv->abase, priv->abase_save); + priv->abase_save = -1; + } + + if (priv->actrl_pbase_save >= 0) { + pci_write_config_byte(dev, priv->actrl_pbase, + priv->actrl_pbase_save); + priv->actrl_pbase_save = -1; } if (priv->gctrl_save >= 0) { @@ -758,9 +767,26 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev) struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, priv->actrl, ®_save); - pci_write_config_byte(dev, priv->actrl, reg_save | 0x80); - priv->actrl_save = reg_save; + switch (lpc_chipset_info[priv->chipset].iTCO_version) { + case 3: + /* + * Some chipsets (eg Avoton) enable the ACPI space in the + * ACPI BASE register. + */ + pci_read_config_byte(dev, priv->abase, ®_save); + pci_write_config_byte(dev, priv->abase, reg_save | 0x2); + priv->abase_save = reg_save; + break; + default: + /* + * Most chipsets enable the ACPI space in the ACPI control + * register. + */ + pci_read_config_byte(dev, priv->actrl_pbase, ®_save); + pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80); + priv->actrl_pbase_save = reg_save; + break; + } } static void lpc_ich_enable_gpio_space(struct pci_dev *dev) @@ -773,6 +799,17 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev) priv->gctrl_save = reg_save; } +static void lpc_ich_enable_pmc_space(struct pci_dev *dev) +{ + struct lpc_ich_priv *priv = pci_get_drvdata(dev); + u8 reg_save; + + pci_read_config_byte(dev, priv->actrl_pbase, ®_save); + pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2); + + priv->actrl_pbase_save = reg_save; +} + static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell) { struct lpc_ich_priv *priv = pci_get_drvdata(dev); @@ -910,14 +947,20 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) lpc_ich_enable_acpi_space(dev); /* + * iTCO v2: * Get the Memory-Mapped GCS register. To get access to it * we have to read RCBA from PCI Config space 0xf0 and use * it as base. GCS = RCBA + ICH6_GCS(0x3410). + * + * iTCO v3: + * Get the Power Management Configuration register. To get access + * to it we have to read the PMC BASE from config space and address + * the register at offset 0x8. */ if (lpc_chipset_info[priv->chipset].iTCO_version == 1) { /* Don't register iomem for TCO ver 1 */ lpc_ich_cells[LPC_WDT].num_resources--; - } else { + } else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) { pci_read_config_dword(dev, RCBABASE, &base_addr_cfg); base_addr = base_addr_cfg & 0xffffc000; if (!(base_addr_cfg & 1)) { @@ -926,9 +969,17 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) ret = -ENODEV; goto wdt_done; } - res = wdt_mem_res(ICH_RES_MEM_GCS); + res = wdt_mem_res(ICH_RES_MEM_GCS_PMC); res->start = base_addr + ACPIBASE_GCS_OFF; res->end = base_addr + ACPIBASE_GCS_END; + } else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) { + lpc_ich_enable_pmc_space(dev); + pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg); + base_addr = base_addr_cfg & 0xfffffe00; + + res = wdt_mem_res(ICH_RES_MEM_GCS_PMC); + res->start = base_addr + ACPIBASE_PMC_OFF; + res->end = base_addr + ACPIBASE_PMC_END; } lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]); @@ -953,9 +1004,11 @@ static int lpc_ich_probe(struct pci_dev *dev, priv->chipset = id->driver_data; - priv->actrl_save = -1; + priv->actrl_pbase_save = -1; + priv->abase_save = -1; + priv->abase = ACPIBASE; - priv->actrl = ACPICTRL; + priv->actrl_pbase = ACPICTRL_PMCBASE; priv->gctrl_save = -1; if (priv->chipset <= LPC_ICH5) { -- cgit v1.2.3 From 24b3a1670b47e75be633ae0b5c07945c446f9d29 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:55 -0500 Subject: watchdog: iTCO_wdt: Add support for v3 silicon Some new Atom's, eg Avoton and Bay Trail, have slightly different iTCO functionality: - The watchdog timer ticks at 1 second instead of .6 seconds - Some 8 and 16-bit registers were combined into 32-bit registers - Some registers were removed (DAT_IN, DAT_OUT, MESSAGE) - The BOOT_STS field in TCO_STS was removed - The NO_REBOOT bit is in the PMC area instead of GCS Update the driver to support the above changes and bump the version to 1.11. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/watchdog/iTCO_wdt.c | 137 ++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 04f8af65acfd..6d5928ffe604 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -48,7 +48,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.10" +#define DRV_VERSION "1.11" /* Includes */ #include /* For module specific items */ @@ -92,9 +92,12 @@ static struct { /* this is private data for the iTCO_wdt device */ unsigned int iTCO_version; struct resource *tco_res; struct resource *smi_res; - struct resource *gcs_res; - /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ - unsigned long __iomem *gcs; + /* + * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2), + * or memory-mapped PMC register bit 4 (TCO version 3). + */ + struct resource *gcs_pmc_res; + unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; struct platform_device *dev; @@ -125,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, * Some TCO specific functions */ -static inline unsigned int seconds_to_ticks(int seconds) +/* + * The iTCO v1 and v2's internal timer is stored as ticks which decrement + * every 0.6 seconds. v3's internal timer is stored as seconds (some + * datasheets incorrectly state 0.6 seconds). + */ +static inline unsigned int seconds_to_ticks(int secs) { - /* the internal timer is stored as ticks which decrement - * every 0.6 seconds */ - return (seconds * 10) / 6; + return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6; +} + +static inline unsigned int ticks_to_seconds(int ticks) +{ + return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10; } static void iTCO_wdt_set_NO_REBOOT_bit(void) @@ -137,10 +148,14 @@ static void iTCO_wdt_set_NO_REBOOT_bit(void) u32 val32; /* Set the NO_REBOOT bit: this disables reboots */ - if (iTCO_wdt_private.iTCO_version == 2) { - val32 = readl(iTCO_wdt_private.gcs); + if (iTCO_wdt_private.iTCO_version == 3) { + val32 = readl(iTCO_wdt_private.gcs_pmc); + val32 |= 0x00000010; + writel(val32, iTCO_wdt_private.gcs_pmc); + } else if (iTCO_wdt_private.iTCO_version == 2) { + val32 = readl(iTCO_wdt_private.gcs_pmc); val32 |= 0x00000020; - writel(val32, iTCO_wdt_private.gcs); + writel(val32, iTCO_wdt_private.gcs_pmc); } else if (iTCO_wdt_private.iTCO_version == 1) { pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); val32 |= 0x00000002; @@ -154,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) u32 val32; /* Unset the NO_REBOOT bit: this enables reboots */ - if (iTCO_wdt_private.iTCO_version == 2) { - val32 = readl(iTCO_wdt_private.gcs); + if (iTCO_wdt_private.iTCO_version == 3) { + val32 = readl(iTCO_wdt_private.gcs_pmc); + val32 &= 0xffffffef; + writel(val32, iTCO_wdt_private.gcs_pmc); + + val32 = readl(iTCO_wdt_private.gcs_pmc); + if (val32 & 0x00000010) + ret = -EIO; + } else if (iTCO_wdt_private.iTCO_version == 2) { + val32 = readl(iTCO_wdt_private.gcs_pmc); val32 &= 0xffffffdf; - writel(val32, iTCO_wdt_private.gcs); + writel(val32, iTCO_wdt_private.gcs_pmc); - val32 = readl(iTCO_wdt_private.gcs); + val32 = readl(iTCO_wdt_private.gcs_pmc); if (val32 & 0x00000020) ret = -EIO; } else if (iTCO_wdt_private.iTCO_version == 1) { @@ -192,7 +215,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) /* Force the timer to its reload value by writing to the TCO_RLD register */ - if (iTCO_wdt_private.iTCO_version == 2) + if (iTCO_wdt_private.iTCO_version >= 2) outw(0x01, TCO_RLD); else if (iTCO_wdt_private.iTCO_version == 1) outb(0x01, TCO_RLD); @@ -240,9 +263,9 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); /* Reload the timer by writing to the TCO Timer Counter register */ - if (iTCO_wdt_private.iTCO_version == 2) + if (iTCO_wdt_private.iTCO_version >= 2) { outw(0x01, TCO_RLD); - else if (iTCO_wdt_private.iTCO_version == 1) { + } else if (iTCO_wdt_private.iTCO_version == 1) { /* Reset the timeout status bit so that the timer * needs to count down twice again before rebooting */ outw(0x0008, TCO1_STS); /* write 1 to clear bit */ @@ -270,14 +293,14 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) /* "Values of 0h-3h are ignored and should not be attempted" */ if (tmrval < 0x04) return -EINVAL; - if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) || + if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) || ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) return -EINVAL; iTCO_vendor_pre_set_heartbeat(tmrval); /* Write new heartbeat to watchdog */ - if (iTCO_wdt_private.iTCO_version == 2) { + if (iTCO_wdt_private.iTCO_version >= 2) { spin_lock(&iTCO_wdt_private.io_lock); val16 = inw(TCOv2_TMR); val16 &= 0xfc00; @@ -312,13 +335,13 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) unsigned int time_left = 0; /* read the TCO Timer */ - if (iTCO_wdt_private.iTCO_version == 2) { + if (iTCO_wdt_private.iTCO_version >= 2) { spin_lock(&iTCO_wdt_private.io_lock); val16 = inw(TCO_RLD); val16 &= 0x3ff; spin_unlock(&iTCO_wdt_private.io_lock); - time_left = (val16 * 6) / 10; + time_left = ticks_to_seconds(val16); } else if (iTCO_wdt_private.iTCO_version == 1) { spin_lock(&iTCO_wdt_private.io_lock); val8 = inb(TCO_RLD); @@ -327,7 +350,7 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) val8 += (inb(TCOv1_TMR) & 0x3f); spin_unlock(&iTCO_wdt_private.io_lock); - time_left = (val8 * 6) / 10; + time_left = ticks_to_seconds(val8); } return time_left; } @@ -376,16 +399,16 @@ static void iTCO_wdt_cleanup(void) resource_size(iTCO_wdt_private.tco_res)); release_region(iTCO_wdt_private.smi_res->start, resource_size(iTCO_wdt_private.smi_res)); - if (iTCO_wdt_private.iTCO_version == 2) { - iounmap(iTCO_wdt_private.gcs); - release_mem_region(iTCO_wdt_private.gcs_res->start, - resource_size(iTCO_wdt_private.gcs_res)); + if (iTCO_wdt_private.iTCO_version >= 2) { + iounmap(iTCO_wdt_private.gcs_pmc); + release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); } iTCO_wdt_private.tco_res = NULL; iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_res = NULL; - iTCO_wdt_private.gcs = NULL; + iTCO_wdt_private.gcs_pmc_res = NULL; + iTCO_wdt_private.gcs_pmc = NULL; } static int iTCO_wdt_probe(struct platform_device *dev) @@ -414,27 +437,27 @@ static int iTCO_wdt_probe(struct platform_device *dev) iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); /* - * Get the Memory-Mapped GCS register, we need it for the - * NO_REBOOT flag (TCO v2). + * Get the Memory-Mapped GCS or PMC register, we need it for the + * NO_REBOOT flag (TCO v2 and v3). */ - if (iTCO_wdt_private.iTCO_version == 2) { - iTCO_wdt_private.gcs_res = platform_get_resource(dev, + if (iTCO_wdt_private.iTCO_version >= 2) { + iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev, IORESOURCE_MEM, - ICH_RES_MEM_GCS); + ICH_RES_MEM_GCS_PMC); - if (!iTCO_wdt_private.gcs_res) + if (!iTCO_wdt_private.gcs_pmc_res) goto out; - if (!request_mem_region(iTCO_wdt_private.gcs_res->start, - resource_size(iTCO_wdt_private.gcs_res), dev->name)) { + if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) { ret = -EBUSY; goto out; } - iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start, - resource_size(iTCO_wdt_private.gcs_res)); - if (!iTCO_wdt_private.gcs) { + iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); + if (!iTCO_wdt_private.gcs_pmc) { ret = -EIO; - goto unreg_gcs; + goto unreg_gcs_pmc; } } @@ -442,7 +465,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto unmap_gcs; + goto unmap_gcs_pmc; } /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ @@ -454,7 +477,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) pr_err("I/O address 0x%04llx already in use, device disabled\n", (u64)SMI_EN); ret = -EBUSY; - goto unmap_gcs; + goto unmap_gcs_pmc; } if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { /* @@ -478,9 +501,13 @@ static int iTCO_wdt_probe(struct platform_device *dev) ich_info->name, ich_info->iTCO_version, (u64)TCOBASE); /* Clear out the (probably old) status */ - outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ - outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ - outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ + if (iTCO_wdt_private.iTCO_version == 3) { + outl(0x20008, TCO1_STS); + } else { + outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ + outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ + } iTCO_wdt_watchdog_dev.bootstatus = 0; iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; @@ -515,18 +542,18 @@ unreg_tco: unreg_smi: release_region(iTCO_wdt_private.smi_res->start, resource_size(iTCO_wdt_private.smi_res)); -unmap_gcs: - if (iTCO_wdt_private.iTCO_version == 2) - iounmap(iTCO_wdt_private.gcs); -unreg_gcs: - if (iTCO_wdt_private.iTCO_version == 2) - release_mem_region(iTCO_wdt_private.gcs_res->start, - resource_size(iTCO_wdt_private.gcs_res)); +unmap_gcs_pmc: + if (iTCO_wdt_private.iTCO_version >= 2) + iounmap(iTCO_wdt_private.gcs_pmc); +unreg_gcs_pmc: + if (iTCO_wdt_private.iTCO_version >= 2) + release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); out: iTCO_wdt_private.tco_res = NULL; iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_res = NULL; - iTCO_wdt_private.gcs = NULL; + iTCO_wdt_private.gcs_pmc_res = NULL; + iTCO_wdt_private.gcs_pmc = NULL; return ret; } -- cgit v1.2.3 From c48cf59878685cc06b71bb2a3ca17b61103c8de7 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:56 -0500 Subject: mfd: lpc_ich: Change Avoton to iTCO v3 The register layout of the Avoton is compatible with the iTCO v3 register layout. Signed-off-by: Peter Tyser Tested-by: Rajat Jain Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index c0683370abbf..b201b0d893ca 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -501,7 +501,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { }, [LPC_AVN] = { .name = "Avoton SoC", - .iTCO_version = 1, + .iTCO_version = 3, .gpio_version = AVOTON_GPIO, }, [LPC_COLETO] = { -- cgit v1.2.3 From 117bbfe25cfc2e968be1f7976ac460a5cd3d734e Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:57 -0500 Subject: mfd: lpc_ich: Add support for NM10 GPIO The NM10's GPIO is compatible with ICH v7 GPIO. Signed-off-by: Peter Tyser Tested-by: Dan Weinlader Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index b201b0d893ca..2f657c7472b2 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -305,6 +305,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { [LPC_NM10] = { .name = "NM10", .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, }, [LPC_ICH8] = { .name = "ICH8 or ICH8R", -- cgit v1.2.3 From 6111ec70357022ccd037399c13f69900431850b4 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Mon, 10 Mar 2014 16:34:58 -0500 Subject: mfd: lpc_ich: Add support for Intel Bay Trail SoC This patch adds the LPC Controller Device IDs for Watchdog and GPIO for the Intel Bay Trail Atom SoC. Signed-off-by: Peter Tyser Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 2f657c7472b2..3f10ea3f45d1 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -213,6 +213,7 @@ enum lpc_chipsets { LPC_LPT_LP, /* Lynx Point-LP */ LPC_WBG, /* Wellsburg */ LPC_AVN, /* Avoton SoC */ + LPC_BAYTRAIL, /* Bay Trail SoC */ LPC_COLETO, /* Coleto Creek */ LPC_WPT_LP, /* Wildcat Point-LP */ }; @@ -505,6 +506,10 @@ static struct lpc_ich_info lpc_chipset_info[] = { .iTCO_version = 3, .gpio_version = AVOTON_GPIO, }, + [LPC_BAYTRAIL] = { + .name = "Bay Trail SoC", + .iTCO_version = 3, + }, [LPC_COLETO] = { .name = "Coleto Creek", .iTCO_version = 2, @@ -730,6 +735,7 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN}, { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN}, { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL}, { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO}, { PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP}, -- cgit v1.2.3 From 3827c510b5a9a97017ecabdfca2ae7a6c29e2385 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 15 Mar 2014 09:24:50 +0800 Subject: mfd: bcm590xx: Fix type argument for module device table This fixes below build error. FATAL: drivers/mfd/bcm590xx: sizeof(struct i2c_device_id)=24 is not a modulo of the size of section __mod_i2c_device_table=392. Fix definition of struct i2c_device_id in mod_devicetable.h make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Signed-off-by: Axel Lin Signed-off-by: Lee Jones --- drivers/mfd/bcm590xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c index 926a57e4d533..e9a33c79431b 100644 --- a/drivers/mfd/bcm590xx.c +++ b/drivers/mfd/bcm590xx.c @@ -68,7 +68,7 @@ static const struct of_device_id bcm590xx_of_match[] = { { .compatible = "brcm,bcm59056" }, { } }; -MODULE_DEVICE_TABLE(i2c, bcm590xx_of_match); +MODULE_DEVICE_TABLE(of, bcm590xx_of_match); static const struct i2c_device_id bcm590xx_i2c_id[] = { { "bcm59056" }, -- cgit v1.2.3 From 3033ee62c0d40561835a6249db80c3eff3a52b0a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 18 Mar 2014 15:56:00 +0100 Subject: mfd: Remove obsolete ti-ssp driver The tnetv107x platform is getting removed, so this driver is not needed any more. Signed-off-by: Arnd Bergmann Acked-by: Sekhar Nori Acked-by: Kevin Hilman Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 11 -- drivers/mfd/Makefile | 1 - drivers/mfd/ti-ssp.c | 464 --------------------------------------------------- 3 files changed, 476 deletions(-) delete mode 100644 drivers/mfd/ti-ssp.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7587c9e1a519..33834120d057 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -789,17 +789,6 @@ config MFD_PALMAS If you say yes here you get support for the Palmas series of PMIC chips from Texas Instruments. -config MFD_TI_SSP - tristate "TI Sequencer Serial Port support" - depends on ARCH_DAVINCI_TNETV107X - select MFD_CORE - ---help--- - Say Y here if you want support for the Sequencer Serial Port - in a Texas Instruments TNETV107X SoC. - - To compile this driver as a module, choose M here: the - module will be called ti-ssp. - config TPS6105X tristate "TI TPS61050/61052 Boost Converters" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 23835dfa615c..2851275e2656 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o -obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c deleted file mode 100644 index 0769ecdd2b7f..000000000000 --- a/drivers/mfd/ti-ssp.c +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs - * - * Copyright (C) 2010 Texas Instruments Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register Offsets */ -#define REG_REV 0x00 -#define REG_IOSEL_1 0x04 -#define REG_IOSEL_2 0x08 -#define REG_PREDIV 0x0c -#define REG_INTR_ST 0x10 -#define REG_INTR_EN 0x14 -#define REG_TEST_CTRL 0x18 - -/* Per port registers */ -#define PORT_CFG_2 0x00 -#define PORT_ADDR 0x04 -#define PORT_DATA 0x08 -#define PORT_CFG_1 0x0c -#define PORT_STATE 0x10 - -#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT) -#define SSP_PORT_CLKRATE_MASK 0x0f - -#define SSP_SEQRAM_WR_EN BIT(4) -#define SSP_SEQRAM_RD_EN BIT(5) -#define SSP_START BIT(15) -#define SSP_BUSY BIT(10) -#define SSP_PORT_ASL BIT(7) -#define SSP_PORT_CFO1 BIT(6) - -#define SSP_PORT_SEQRAM_SIZE 32 - -static const int ssp_port_base[] = {0x040, 0x080}; -static const int ssp_port_seqram[] = {0x100, 0x180}; - -struct ti_ssp { - struct resource *res; - struct device *dev; - void __iomem *regs; - spinlock_t lock; - struct clk *clk; - int irq; - wait_queue_head_t wqh; - - /* - * Some of the iosel2 register bits always read-back as 0, we need to - * remember these values so that we don't clobber previously set - * values. - */ - u32 iosel2; -}; - -static inline struct ti_ssp *dev_to_ssp(struct device *dev) -{ - return dev_get_drvdata(dev->parent); -} - -static inline int dev_to_port(struct device *dev) -{ - return to_platform_device(dev)->id; -} - -/* Register Access Helpers, rmw() functions need to run locked */ -static inline u32 ssp_read(struct ti_ssp *ssp, int reg) -{ - return __raw_readl(ssp->regs + reg); -} - -static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) -{ - __raw_writel(val, ssp->regs + reg); -} - -static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) -{ - ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); -} - -static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) -{ - return ssp_read(ssp, ssp_port_base[port] + reg); -} - -static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, - u32 val) -{ - ssp_write(ssp, ssp_port_base[port] + reg, val); -} - -static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, - u32 mask, u32 bits) -{ - ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); -} - -static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, - u32 bits) -{ - ssp_port_rmw(ssp, port, reg, bits, 0); -} - -static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, - u32 bits) -{ - ssp_port_rmw(ssp, port, reg, 0, bits); -} - -/* Called to setup port clock mode, caller must hold ssp->lock */ -static int __set_mode(struct ti_ssp *ssp, int port, int mode) -{ - mode &= SSP_PORT_CONFIG_MASK; - ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); - - return 0; -} - -int ti_ssp_set_mode(struct device *dev, int mode) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev); - int ret; - - spin_lock(&ssp->lock); - ret = __set_mode(ssp, port, mode); - spin_unlock(&ssp->lock); - - return ret; -} -EXPORT_SYMBOL(ti_ssp_set_mode); - -/* Called to setup iosel2, caller must hold ssp->lock */ -static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) -{ - ssp->iosel2 = (ssp->iosel2 & ~mask) | val; - ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); -} - -/* Called to setup port iosel, caller must hold ssp->lock */ -static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) -{ - unsigned val, shift = port ? 16 : 0; - - /* IOSEL1 gets the least significant 16 bits */ - val = ssp_read(ssp, REG_IOSEL_1); - val &= 0xffff << (port ? 0 : 16); - val |= (iosel & 0xffff) << (port ? 16 : 0); - ssp_write(ssp, REG_IOSEL_1, val); - - /* IOSEL2 gets the most significant 16 bits */ - val = (iosel >> 16) & 0x7; - __set_iosel2(ssp, 0x7 << shift, val << shift); -} - -int ti_ssp_set_iosel(struct device *dev, u32 iosel) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev); - - spin_lock(&ssp->lock); - __set_iosel(ssp, port, iosel); - spin_unlock(&ssp->lock); - - return 0; -} -EXPORT_SYMBOL(ti_ssp_set_iosel); - -int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev); - int i; - - if (len > SSP_PORT_SEQRAM_SIZE) - return -ENOSPC; - - spin_lock(&ssp->lock); - - /* Enable SeqRAM access */ - ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); - - /* Copy code */ - for (i = 0; i < len; i++) { - __raw_writel(prog[i], ssp->regs + offs + 4*i + - ssp_port_seqram[port]); - } - - /* Disable SeqRAM access */ - ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); - - spin_unlock(&ssp->lock); - - return 0; -} -EXPORT_SYMBOL(ti_ssp_load); - -int ti_ssp_raw_read(struct device *dev) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev); - int shift = port ? 27 : 11; - - return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; -} -EXPORT_SYMBOL(ti_ssp_raw_read); - -int ti_ssp_raw_write(struct device *dev, u32 val) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev), shift; - - spin_lock(&ssp->lock); - - shift = port ? 22 : 6; - val &= 0xf; - __set_iosel2(ssp, 0xf << shift, val << shift); - - spin_unlock(&ssp->lock); - - return 0; -} -EXPORT_SYMBOL(ti_ssp_raw_write); - -static inline int __xfer_done(struct ti_ssp *ssp, int port) -{ - return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); -} - -int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) -{ - struct ti_ssp *ssp = dev_to_ssp(dev); - int port = dev_to_port(dev); - int ret; - - if (pc & ~(0x3f)) - return -EINVAL; - - /* Grab ssp->lock to serialize rmw on ssp registers */ - spin_lock(&ssp->lock); - - ssp_port_write(ssp, port, PORT_ADDR, input >> 16); - ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); - ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); - - /* grab wait queue head lock to avoid race with the isr */ - spin_lock_irq(&ssp->wqh.lock); - - /* kick off sequence execution in hardware */ - ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); - - /* drop ssp lock; no register writes beyond this */ - spin_unlock(&ssp->lock); - - ret = wait_event_interruptible_locked_irq(ssp->wqh, - __xfer_done(ssp, port)); - spin_unlock_irq(&ssp->wqh.lock); - - if (ret < 0) - return ret; - - if (output) { - *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | - (ssp_port_read(ssp, port, PORT_DATA) & 0xffff); - } - - ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ - - return ret; -} -EXPORT_SYMBOL(ti_ssp_run); - -static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) -{ - struct ti_ssp *ssp = dev_data; - - spin_lock(&ssp->wqh.lock); - - ssp_write(ssp, REG_INTR_ST, 0x3); - wake_up_locked(&ssp->wqh); - - spin_unlock(&ssp->wqh.lock); - - return IRQ_HANDLED; -} - -static int ti_ssp_probe(struct platform_device *pdev) -{ - static struct ti_ssp *ssp; - const struct ti_ssp_data *pdata = dev_get_platdata(&pdev->dev); - int error = 0, prediv = 0xff, id; - unsigned long sysclk; - struct device *dev = &pdev->dev; - struct mfd_cell cells[2]; - - ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); - if (!ssp) { - dev_err(dev, "cannot allocate device info\n"); - return -ENOMEM; - } - - ssp->dev = dev; - dev_set_drvdata(dev, ssp); - - ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ssp->res) { - error = -ENODEV; - dev_err(dev, "cannot determine register area\n"); - goto error_res; - } - - if (!request_mem_region(ssp->res->start, resource_size(ssp->res), - pdev->name)) { - error = -ENOMEM; - dev_err(dev, "cannot claim register memory\n"); - goto error_res; - } - - ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); - if (!ssp->regs) { - error = -ENOMEM; - dev_err(dev, "cannot map register memory\n"); - goto error_map; - } - - ssp->clk = clk_get(dev, NULL); - if (IS_ERR(ssp->clk)) { - error = PTR_ERR(ssp->clk); - dev_err(dev, "cannot claim device clock\n"); - goto error_clk; - } - - ssp->irq = platform_get_irq(pdev, 0); - if (ssp->irq < 0) { - error = -ENODEV; - dev_err(dev, "unknown irq\n"); - goto error_irq; - } - - error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, - dev_name(dev), ssp); - if (error < 0) { - dev_err(dev, "cannot acquire irq\n"); - goto error_irq; - } - - spin_lock_init(&ssp->lock); - init_waitqueue_head(&ssp->wqh); - - /* Power on and initialize SSP */ - error = clk_enable(ssp->clk); - if (error) { - dev_err(dev, "cannot enable device clock\n"); - goto error_enable; - } - - /* Reset registers to a sensible known state */ - ssp_write(ssp, REG_IOSEL_1, 0); - ssp_write(ssp, REG_IOSEL_2, 0); - ssp_write(ssp, REG_INTR_EN, 0x3); - ssp_write(ssp, REG_INTR_ST, 0x3); - ssp_write(ssp, REG_TEST_CTRL, 0); - ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); - ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); - ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); - ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); - - sysclk = clk_get_rate(ssp->clk); - if (pdata && pdata->out_clock) - prediv = (sysclk / pdata->out_clock) - 1; - prediv = clamp(prediv, 0, 0xff); - ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); - - memset(cells, 0, sizeof(cells)); - for (id = 0; id < 2; id++) { - const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; - - cells[id].id = id; - cells[id].name = data->dev_name; - cells[id].platform_data = data->pdata; - } - - error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL); - if (error < 0) { - dev_err(dev, "cannot add mfd cells\n"); - goto error_enable; - } - - return 0; - -error_enable: - free_irq(ssp->irq, ssp); -error_irq: - clk_put(ssp->clk); -error_clk: - iounmap(ssp->regs); -error_map: - release_mem_region(ssp->res->start, resource_size(ssp->res)); -error_res: - kfree(ssp); - return error; -} - -static int ti_ssp_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ti_ssp *ssp = dev_get_drvdata(dev); - - mfd_remove_devices(dev); - clk_disable(ssp->clk); - free_irq(ssp->irq, ssp); - clk_put(ssp->clk); - iounmap(ssp->regs); - release_mem_region(ssp->res->start, resource_size(ssp->res)); - kfree(ssp); - return 0; -} - -static struct platform_driver ti_ssp_driver = { - .probe = ti_ssp_probe, - .remove = ti_ssp_remove, - .driver = { - .name = "ti-ssp", - .owner = THIS_MODULE, - } -}; - -module_platform_driver(ti_ssp_driver); - -MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ti-ssp"); -- cgit v1.2.3 From 53c31b3437a6400c6ffc2c9315680217ad84cb6d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Mar 2014 10:19:17 +0100 Subject: mfd: sec-core: Add of_compatible strings for clock MFD cells Add of_compatible strings for S5M8767 and S2MPS14 clock MFD cells. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Tomasz Figa Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 281a82747275..e9bf73ba6e69 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -60,6 +60,7 @@ static const struct mfd_cell s5m8767_devs[] = { .name = "s5m-rtc", }, { .name = "s5m8767-clk", + .of_compatible = "samsung,s5m8767-clk", } }; @@ -68,6 +69,7 @@ static const struct mfd_cell s2mps11_devs[] = { .name = "s2mps11-pmic", }, { .name = "s2mps11-clk", + .of_compatible = "samsung,s2mps11-clk", } }; @@ -78,6 +80,7 @@ static const struct mfd_cell s2mps14_devs[] = { .name = "s2mps14-rtc", }, { .name = "s2mps14-clk", + .of_compatible = "samsung,s2mps14-clk", } }; -- cgit v1.2.3 From 8c66eeced1989c98104783832ab445305fa8c7a3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 18 Mar 2014 13:57:50 +0100 Subject: mfd: sec-core: Fix I2C dummy device resource leak on probe failure Dummy I2C device allocated in sec_pmic_probe() leaked if devm_regmap_init_i2c() failed. Unregister it before returning from probe. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index e9bf73ba6e69..91405e94cfee 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -347,7 +347,7 @@ static int sec_pmic_probe(struct i2c_client *i2c, ret = PTR_ERR(sec_pmic->regmap_rtc); dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n", ret); - return ret; + goto err_regmap_rtc; } if (pdata && pdata->cfg_pmic_irq) @@ -388,14 +388,15 @@ static int sec_pmic_probe(struct i2c_client *i2c, } if (ret) - goto err; + goto err_mfd; device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup); return ret; -err: +err_mfd: sec_irq_exit(sec_pmic); +err_regmap_rtc: i2c_unregister_device(sec_pmic->rtc); return ret; } -- cgit v1.2.3 From 483e2dfdbc94751430e41db9973985f5b054d322 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Mar 2014 16:50:44 +0100 Subject: mfd: tps65910: Fix possible invalid pointer dereference on regmap_add_irq_chip fail Fixes: 4aab3fadad32 ("mfd: tps65910: Move interrupt implementation code to mfd file") tps65910_irq_init() sets 'tps65910->chip_irq' before calling regmap_add_irq_chip(). If the regmap_add_irq_chip() call fails in memory allocation of regmap_irq_chip_data members then: 1. The 'tps65910->chip_irq' will still hold some value 2. 'tps65910->irq_data' will be pointing to already freed memory (because regmap_add_irq_chip() will free it on error) This results in invalid memory access during driver remove because the tps65910_irq_exit() tests whether 'tps65910->chip_irq' is not zero. Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/tps65910.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 1f142d76cbbc..d6573318977f 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -255,8 +255,10 @@ static int tps65910_irq_init(struct tps65910 *tps65910, int irq, ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq, IRQF_ONESHOT, pdata->irq_base, tps6591x_irqs_chip, &tps65910->irq_data); - if (ret < 0) + if (ret < 0) { dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret); + tps65910->chip_irq = 0; + } return ret; } -- cgit v1.2.3 From 742766aac563ea1c28e9f86ed53db757a2872081 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Mar 2014 16:50:45 +0100 Subject: mfd: tps65910: Fix regmap_irq_chip_data leak on mfd_add_devices fail The tps65910_i2c_probe() allocates regmap_irq_chip in tps65910_irq_init() but it does not clean this up in case of mfd_add_devices() failure. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/tps65910.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index d6573318977f..460a014ca629 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -511,6 +511,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, regmap_irq_get_domain(tps65910->irq_data)); if (ret < 0) { dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + tps65910_irq_exit(tps65910); return ret; } -- cgit v1.2.3 From 5a78401623740c892868d5929b33f5cda8fe819e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 18 Mar 2014 14:11:26 +0100 Subject: mfd: sec-core: Fix uninitialized 'regmap_rtc' on S2MPA01 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize the 'regmap_rtc' on S2MPA01 to some sane value. Sane at least for S5M87X chipsets, not S2MPS/S2MPA but it won't be used because rtc-s5m driver does not support S2MPA01. This fixes following error: drivers/mfd/sec-core.c:342:45: warning: ‘regmap_rtc’ may be used uninitialized in this function [-Wuninitialized] Signed-off-by: Krzysztof Kozlowski Acked-by: Sachin Kamat Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 91405e94cfee..1cf27521fff4 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -298,6 +298,13 @@ static int sec_pmic_probe(struct i2c_client *i2c, switch (sec_pmic->device_type) { case S2MPA01: regmap = &s2mpa01_regmap_config; + /* + * The rtc-s5m driver does not support S2MPA01 and there + * is no mfd_cell for S2MPA01 RTC device. + * However we must pass something to devm_regmap_init_i2c() + * so use S5M-like regmap config even though it wouldn't work. + */ + regmap_rtc = &s5m_rtc_regmap_config; break; case S2MPS11X: regmap = &s2mps11_regmap_config; -- cgit v1.2.3 From 204747c970c0d568721c76ab8a57dde0e5dcf0d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 20 Mar 2014 08:12:28 -0700 Subject: mfd: kempld-core: Fix potential hang-up during boot On PXT and COMe-cPC2 boards it is observed that the hardware mutex is acquired but not being released during initialization. This can result in a hang-up during boot if the driver is built into the kernel. Releasing the mutex twice if it was acquired fixes the problem. Subsequent request/release cycles work as expected, so the fix is only needed during initialization. Cc: Reviewed-by: Michael Brunner Tested-by: Michael Brunner Signed-off-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/kempld-core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index 20a6afcfb36c..07692604e119 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -322,9 +322,12 @@ static int kempld_detect_device(struct kempld_device_data *pld) return -ENODEV; } - /* Release hardware mutex if aquired */ - if (!(index_reg & KEMPLD_MUTEX_KEY)) + /* Release hardware mutex if acquired */ + if (!(index_reg & KEMPLD_MUTEX_KEY)) { iowrite8(KEMPLD_MUTEX_KEY, pld->io_index); + /* PXT and COMe-cPC2 boards may require a second release */ + iowrite8(KEMPLD_MUTEX_KEY, pld->io_index); + } mutex_unlock(&pld->lock); -- cgit v1.2.3 From a0eae337cf27432f7a116e8eba67e69e22f2fd07 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 20 Mar 2014 16:49:24 +0000 Subject: mfd: arizona: Mark DSP clocking register as volatile The DSPs will often control there own clock speeds whilst running as such we should mark the registers controlling this as volatile. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5102-tables.c | 1 + drivers/mfd/wm5110-tables.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 187ee86cbbe6..a856ac720827 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1925,6 +1925,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_AOD_IRQ1: case ARIZONA_AOD_IRQ2: case ARIZONA_AOD_IRQ_RAW_STATUS: + case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index ca9968525017..7611334c612d 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -2652,6 +2652,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_SCRATCH_1: case ARIZONA_DSP1_SCRATCH_2: case ARIZONA_DSP1_SCRATCH_3: + case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP2_STATUS_1: case ARIZONA_DSP2_STATUS_2: case ARIZONA_DSP2_STATUS_3: @@ -2680,6 +2681,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP2_SCRATCH_1: case ARIZONA_DSP2_SCRATCH_2: case ARIZONA_DSP2_SCRATCH_3: + case ARIZONA_DSP2_CLOCKING_1: case ARIZONA_DSP3_STATUS_1: case ARIZONA_DSP3_STATUS_2: case ARIZONA_DSP3_STATUS_3: @@ -2708,6 +2710,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP3_SCRATCH_1: case ARIZONA_DSP3_SCRATCH_2: case ARIZONA_DSP3_SCRATCH_3: + case ARIZONA_DSP3_CLOCKING_1: case ARIZONA_DSP4_STATUS_1: case ARIZONA_DSP4_STATUS_2: case ARIZONA_DSP4_STATUS_3: @@ -2736,6 +2739,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP4_SCRATCH_1: case ARIZONA_DSP4_SCRATCH_2: case ARIZONA_DSP4_SCRATCH_3: + case ARIZONA_DSP4_CLOCKING_1: return true; default: return wm5110_is_adsp_memory(dev, reg); -- cgit v1.2.3 From 2d28ca731b9bb6262f7711241628c7844b0cf7dc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 20 Mar 2014 16:49:23 +0000 Subject: mfd: wm5110: Correct default for HEADPHONE_DETECT_1 Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5110-tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 7611334c612d..1942b6f231da 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -538,7 +538,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ - { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x0000029B, 0x0028 }, /* R667 - Headphone Detect 1 */ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ { 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ -- cgit v1.2.3