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