Loading Documentation/devicetree/bindings/platform/msm/spss.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line MSM Standby Processing Sub-System (SPSS) Controller Required properties: - compatible : Should be "qcom,msm-spss" - reg : Offset and length of the register region for the device - reg-names : Register region name referenced in 'reg' above The only required register resource entry is: "base" : SPSS controller register block The SPSS device may optionally have either or both of GENI-IR and HDMI-CEC devices as the child devices. The address-cells, size-cells and ranges properties ensure that the SPSS child devices are properly enumerated. Example: qcom,msm-spss@fc5c3000 { #address-cells = <1>; #size-cells = <1>; ranges; compatible = "qcom,msm-spss"; reg-names = "base"; reg = <0xfc5c3000 0x1000>; geni_ir_1: qcom,msm-geni-ir@fc5c1000 { ... }; }; drivers/platform/msm/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -159,4 +159,10 @@ config QCA1530 To compile this driver as a module, choose M here: the module will be called qca1530. config MSM_SPSS tristate "MSM SPSS driver" default n help Support for Standby Processing Sub-System (SPSS) module. endmenu drivers/platform/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -19,3 +19,4 @@ obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o obj-$(CONFIG_QPNP_USB_DETECT) += qpnp-usbdetect.o obj-$(CONFIG_QCA1530) += qca1530.o obj-$(CONFIG_KLM) += msm_klmd.o obj-$(CONFIG_MSM_SPSS) += spss.o drivers/platform/msm/spss.c 0 → 100644 +189 −0 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. 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 <linux/clk.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <mach/msm_iomap.h> #define CLOCK_CONTROL_REG 0x00 #define BUS_SMCBC_REG 0x04 #define PSCBC_BUS_REG 0x0C #define PSCBC_GENI_REG 0x10 #define STANDBY_MODE 0x2 #define ACTIVE_MODE 0x1 #define ASYNC_SW_CLK_EN 0x2 struct msm_spss_dev_t { void __iomem *base; struct clk *clk; }; static struct msm_spss_dev_t msm_spss_dev; static int msm_spss_probe(struct platform_device *pdev) { struct resource *res; int rc = 0; msm_spss_dev.clk = clk_get(&pdev->dev, "iface_clk"); if (IS_ERR(msm_spss_dev.clk)) { rc = PTR_ERR(msm_spss_dev.clk); dev_err(&pdev->dev, "could not get ahb clk %d\n", rc); goto err_clk_get; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(&pdev->dev, "missing memory resource\n"); rc = -EINVAL; goto err_res; } msm_spss_dev.base = ioremap(res->start, resource_size(res)); if (!msm_spss_dev.base) { dev_err(&pdev->dev, "ioremap failed\n"); rc = -ENOMEM; goto err_ioremap; } rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (rc) { dev_err(&pdev->dev, "of_platform_populate failed %d\n", rc); goto err_of_plat_pop; } rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); goto err_clk_en; } writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + BUS_SMCBC_REG)); writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + PSCBC_BUS_REG)); writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + PSCBC_GENI_REG)); clk_disable_unprepare(msm_spss_dev.clk); return 0; err_clk_en: err_of_plat_pop: iounmap(msm_spss_dev.base); err_ioremap: err_res: clk_put(msm_spss_dev.clk); err_clk_get: return rc; } static int __exit msm_spss_remove(struct platform_device *pdev) { if (msm_spss_dev.base) iounmap(msm_spss_dev.base); if (msm_spss_dev.clk) clk_put(msm_spss_dev.clk); return 0; } #ifdef CONFIG_PM_SLEEP static int msm_spss_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); int rc; u32 val; rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); return rc; } val = readl_relaxed(msm_spss_dev.base + CLOCK_CONTROL_REG); val &= ~ACTIVE_MODE; val |= STANDBY_MODE; writel_relaxed(val, (msm_spss_dev.base + CLOCK_CONTROL_REG)); wmb(); clk_disable_unprepare(msm_spss_dev.clk); return 0; } static int msm_spss_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); int rc; u32 val; rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); return rc; } val = readl_relaxed(msm_spss_dev.base + CLOCK_CONTROL_REG); val &= ~STANDBY_MODE; val |= ACTIVE_MODE; writel_relaxed(val, (msm_spss_dev.base + CLOCK_CONTROL_REG)); wmb(); clk_disable_unprepare(msm_spss_dev.clk); return 0; } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops msm_spss_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS( msm_spss_suspend, msm_spss_resume ) }; static struct of_device_id msm_spss_match[] = { { .compatible = "qcom,msm-spss", }, {} }; static struct platform_driver msm_spss_driver = { .probe = msm_spss_probe, .remove = msm_spss_remove, .driver = { .name = "msm_spss", .owner = THIS_MODULE, .pm = &msm_spss_dev_pm_ops, .of_match_table = msm_spss_match, }, }; static int __init spss_init(void) { return platform_driver_register(&msm_spss_driver); } static void __exit spss_exit(void) { platform_driver_unregister(&msm_spss_driver); } module_init(spss_init); module_exit(spss_exit); MODULE_LICENSE("GPL v2"); Loading
Documentation/devicetree/bindings/platform/msm/spss.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line MSM Standby Processing Sub-System (SPSS) Controller Required properties: - compatible : Should be "qcom,msm-spss" - reg : Offset and length of the register region for the device - reg-names : Register region name referenced in 'reg' above The only required register resource entry is: "base" : SPSS controller register block The SPSS device may optionally have either or both of GENI-IR and HDMI-CEC devices as the child devices. The address-cells, size-cells and ranges properties ensure that the SPSS child devices are properly enumerated. Example: qcom,msm-spss@fc5c3000 { #address-cells = <1>; #size-cells = <1>; ranges; compatible = "qcom,msm-spss"; reg-names = "base"; reg = <0xfc5c3000 0x1000>; geni_ir_1: qcom,msm-geni-ir@fc5c1000 { ... }; };
drivers/platform/msm/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -159,4 +159,10 @@ config QCA1530 To compile this driver as a module, choose M here: the module will be called qca1530. config MSM_SPSS tristate "MSM SPSS driver" default n help Support for Standby Processing Sub-System (SPSS) module. endmenu
drivers/platform/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -19,3 +19,4 @@ obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o obj-$(CONFIG_QPNP_USB_DETECT) += qpnp-usbdetect.o obj-$(CONFIG_QCA1530) += qca1530.o obj-$(CONFIG_KLM) += msm_klmd.o obj-$(CONFIG_MSM_SPSS) += spss.o
drivers/platform/msm/spss.c 0 → 100644 +189 −0 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. 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 <linux/clk.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <mach/msm_iomap.h> #define CLOCK_CONTROL_REG 0x00 #define BUS_SMCBC_REG 0x04 #define PSCBC_BUS_REG 0x0C #define PSCBC_GENI_REG 0x10 #define STANDBY_MODE 0x2 #define ACTIVE_MODE 0x1 #define ASYNC_SW_CLK_EN 0x2 struct msm_spss_dev_t { void __iomem *base; struct clk *clk; }; static struct msm_spss_dev_t msm_spss_dev; static int msm_spss_probe(struct platform_device *pdev) { struct resource *res; int rc = 0; msm_spss_dev.clk = clk_get(&pdev->dev, "iface_clk"); if (IS_ERR(msm_spss_dev.clk)) { rc = PTR_ERR(msm_spss_dev.clk); dev_err(&pdev->dev, "could not get ahb clk %d\n", rc); goto err_clk_get; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(&pdev->dev, "missing memory resource\n"); rc = -EINVAL; goto err_res; } msm_spss_dev.base = ioremap(res->start, resource_size(res)); if (!msm_spss_dev.base) { dev_err(&pdev->dev, "ioremap failed\n"); rc = -ENOMEM; goto err_ioremap; } rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (rc) { dev_err(&pdev->dev, "of_platform_populate failed %d\n", rc); goto err_of_plat_pop; } rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); goto err_clk_en; } writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + BUS_SMCBC_REG)); writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + PSCBC_BUS_REG)); writel_relaxed(ASYNC_SW_CLK_EN, (msm_spss_dev.base + PSCBC_GENI_REG)); clk_disable_unprepare(msm_spss_dev.clk); return 0; err_clk_en: err_of_plat_pop: iounmap(msm_spss_dev.base); err_ioremap: err_res: clk_put(msm_spss_dev.clk); err_clk_get: return rc; } static int __exit msm_spss_remove(struct platform_device *pdev) { if (msm_spss_dev.base) iounmap(msm_spss_dev.base); if (msm_spss_dev.clk) clk_put(msm_spss_dev.clk); return 0; } #ifdef CONFIG_PM_SLEEP static int msm_spss_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); int rc; u32 val; rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); return rc; } val = readl_relaxed(msm_spss_dev.base + CLOCK_CONTROL_REG); val &= ~ACTIVE_MODE; val |= STANDBY_MODE; writel_relaxed(val, (msm_spss_dev.base + CLOCK_CONTROL_REG)); wmb(); clk_disable_unprepare(msm_spss_dev.clk); return 0; } static int msm_spss_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); int rc; u32 val; rc = clk_prepare_enable(msm_spss_dev.clk); if (rc) { dev_err(&pdev->dev, "ahb clk enable failed %d\n", rc); return rc; } val = readl_relaxed(msm_spss_dev.base + CLOCK_CONTROL_REG); val &= ~STANDBY_MODE; val |= ACTIVE_MODE; writel_relaxed(val, (msm_spss_dev.base + CLOCK_CONTROL_REG)); wmb(); clk_disable_unprepare(msm_spss_dev.clk); return 0; } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops msm_spss_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS( msm_spss_suspend, msm_spss_resume ) }; static struct of_device_id msm_spss_match[] = { { .compatible = "qcom,msm-spss", }, {} }; static struct platform_driver msm_spss_driver = { .probe = msm_spss_probe, .remove = msm_spss_remove, .driver = { .name = "msm_spss", .owner = THIS_MODULE, .pm = &msm_spss_dev_pm_ops, .of_match_table = msm_spss_match, }, }; static int __init spss_init(void) { return platform_driver_register(&msm_spss_driver); } static void __exit spss_exit(void) { platform_driver_unregister(&msm_spss_driver); } module_init(spss_init); module_exit(spss_exit); MODULE_LICENSE("GPL v2");