linux/drivers/mfd/exynos-lpass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
   4 *
   5 * Authors: Inha Song <ideal.song@samsung.com>
   6 *          Sylwester Nawrocki <s.nawrocki@samsung.com>
   7 *
   8 * Samsung Exynos SoC series Low Power Audio Subsystem driver.
   9 *
  10 * This module provides regmap for the Top SFR region and instantiates
  11 * devices for IP blocks like DMAC, I2S, UART.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/io.h>
  17#include <linux/module.h>
  18#include <linux/mfd/syscon.h>
  19#include <linux/of.h>
  20#include <linux/of_platform.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm_runtime.h>
  23#include <linux/regmap.h>
  24#include <linux/soc/samsung/exynos-regs-pmu.h>
  25#include <linux/types.h>
  26
  27/* LPASS Top register definitions */
  28#define SFR_LPASS_CORE_SW_RESET         0x08
  29#define  LPASS_SB_SW_RESET              BIT(11)
  30#define  LPASS_UART_SW_RESET            BIT(10)
  31#define  LPASS_PCM_SW_RESET             BIT(9)
  32#define  LPASS_I2S_SW_RESET             BIT(8)
  33#define  LPASS_WDT1_SW_RESET            BIT(4)
  34#define  LPASS_WDT0_SW_RESET            BIT(3)
  35#define  LPASS_TIMER_SW_RESET           BIT(2)
  36#define  LPASS_MEM_SW_RESET             BIT(1)
  37#define  LPASS_DMA_SW_RESET             BIT(0)
  38
  39#define SFR_LPASS_INTR_CA5_MASK         0x48
  40#define SFR_LPASS_INTR_CPU_MASK         0x58
  41#define  LPASS_INTR_APM                 BIT(9)
  42#define  LPASS_INTR_MIF                 BIT(8)
  43#define  LPASS_INTR_TIMER               BIT(7)
  44#define  LPASS_INTR_DMA                 BIT(6)
  45#define  LPASS_INTR_GPIO                BIT(5)
  46#define  LPASS_INTR_I2S                 BIT(4)
  47#define  LPASS_INTR_PCM                 BIT(3)
  48#define  LPASS_INTR_SLIMBUS             BIT(2)
  49#define  LPASS_INTR_UART                BIT(1)
  50#define  LPASS_INTR_SFR                 BIT(0)
  51
  52struct exynos_lpass {
  53        /* pointer to the LPASS TOP regmap */
  54        struct regmap *top;
  55        struct clk *sfr0_clk;
  56};
  57
  58static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask)
  59{
  60        unsigned int val = 0;
  61
  62        regmap_read(lpass->top, SFR_LPASS_CORE_SW_RESET, &val);
  63
  64        val &= ~mask;
  65        regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val);
  66
  67        usleep_range(100, 150);
  68
  69        val |= mask;
  70        regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val);
  71}
  72
  73static void exynos_lpass_enable(struct exynos_lpass *lpass)
  74{
  75        clk_prepare_enable(lpass->sfr0_clk);
  76
  77        /* Unmask SFR, DMA and I2S interrupt */
  78        regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK,
  79                     LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S);
  80
  81        regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK,
  82                     LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S |
  83                     LPASS_INTR_UART);
  84
  85        exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET);
  86        exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET);
  87        exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET);
  88        exynos_lpass_core_sw_reset(lpass, LPASS_UART_SW_RESET);
  89}
  90
  91static void exynos_lpass_disable(struct exynos_lpass *lpass)
  92{
  93        /* Mask any unmasked IP interrupt sources */
  94        regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0);
  95        regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0);
  96
  97        clk_disable_unprepare(lpass->sfr0_clk);
  98}
  99
 100static const struct regmap_config exynos_lpass_reg_conf = {
 101        .reg_bits       = 32,
 102        .reg_stride     = 4,
 103        .val_bits       = 32,
 104        .max_register   = 0xfc,
 105        .fast_io        = true,
 106};
 107
 108static int exynos_lpass_probe(struct platform_device *pdev)
 109{
 110        struct device *dev = &pdev->dev;
 111        struct exynos_lpass *lpass;
 112        void __iomem *base_top;
 113        struct resource *res;
 114
 115        lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL);
 116        if (!lpass)
 117                return -ENOMEM;
 118
 119        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 120        base_top = devm_ioremap_resource(dev, res);
 121        if (IS_ERR(base_top))
 122                return PTR_ERR(base_top);
 123
 124        lpass->sfr0_clk = devm_clk_get(dev, "sfr0_ctrl");
 125        if (IS_ERR(lpass->sfr0_clk))
 126                return PTR_ERR(lpass->sfr0_clk);
 127
 128        lpass->top = regmap_init_mmio(dev, base_top,
 129                                        &exynos_lpass_reg_conf);
 130        if (IS_ERR(lpass->top)) {
 131                dev_err(dev, "LPASS top regmap initialization failed\n");
 132                return PTR_ERR(lpass->top);
 133        }
 134
 135        platform_set_drvdata(pdev, lpass);
 136        pm_runtime_set_active(dev);
 137        pm_runtime_enable(dev);
 138        exynos_lpass_enable(lpass);
 139
 140        return devm_of_platform_populate(dev);
 141}
 142
 143static int exynos_lpass_remove(struct platform_device *pdev)
 144{
 145        struct exynos_lpass *lpass = platform_get_drvdata(pdev);
 146
 147        exynos_lpass_disable(lpass);
 148        pm_runtime_disable(&pdev->dev);
 149        if (!pm_runtime_status_suspended(&pdev->dev))
 150                exynos_lpass_disable(lpass);
 151        regmap_exit(lpass->top);
 152
 153        return 0;
 154}
 155
 156static int __maybe_unused exynos_lpass_suspend(struct device *dev)
 157{
 158        struct exynos_lpass *lpass = dev_get_drvdata(dev);
 159
 160        exynos_lpass_disable(lpass);
 161
 162        return 0;
 163}
 164
 165static int __maybe_unused exynos_lpass_resume(struct device *dev)
 166{
 167        struct exynos_lpass *lpass = dev_get_drvdata(dev);
 168
 169        exynos_lpass_enable(lpass);
 170
 171        return 0;
 172}
 173
 174static const struct dev_pm_ops lpass_pm_ops = {
 175        SET_RUNTIME_PM_OPS(exynos_lpass_suspend, exynos_lpass_resume, NULL)
 176        SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 177                                     pm_runtime_force_resume)
 178};
 179
 180static const struct of_device_id exynos_lpass_of_match[] = {
 181        { .compatible = "samsung,exynos5433-lpass" },
 182        { },
 183};
 184MODULE_DEVICE_TABLE(of, exynos_lpass_of_match);
 185
 186static struct platform_driver exynos_lpass_driver = {
 187        .driver = {
 188                .name           = "exynos-lpass",
 189                .pm             = &lpass_pm_ops,
 190                .of_match_table = exynos_lpass_of_match,
 191        },
 192        .probe  = exynos_lpass_probe,
 193        .remove = exynos_lpass_remove,
 194};
 195module_platform_driver(exynos_lpass_driver);
 196
 197MODULE_DESCRIPTION("Samsung Low Power Audio Subsystem driver");
 198MODULE_LICENSE("GPL v2");
 199