linux/drivers/nvmem/qcom-spmi-sdam.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2017, 2020-2021, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/device.h>
   7#include <linux/module.h>
   8#include <linux/of.h>
   9#include <linux/of_platform.h>
  10#include <linux/nvmem-provider.h>
  11#include <linux/regmap.h>
  12
  13#define SDAM_MEM_START                  0x40
  14#define REGISTER_MAP_ID                 0x40
  15#define REGISTER_MAP_VERSION            0x41
  16#define SDAM_SIZE                       0x44
  17#define SDAM_PBS_TRIG_SET               0xE5
  18#define SDAM_PBS_TRIG_CLR               0xE6
  19
  20struct sdam_chip {
  21        struct regmap                   *regmap;
  22        struct nvmem_config             sdam_config;
  23        unsigned int                    base;
  24        unsigned int                    size;
  25};
  26
  27/* read only register offsets */
  28static const u8 sdam_ro_map[] = {
  29        REGISTER_MAP_ID,
  30        REGISTER_MAP_VERSION,
  31        SDAM_SIZE
  32};
  33
  34static bool sdam_is_valid(struct sdam_chip *sdam, unsigned int offset,
  35                                size_t len)
  36{
  37        unsigned int sdam_mem_end = SDAM_MEM_START + sdam->size - 1;
  38
  39        if (!len)
  40                return false;
  41
  42        if (offset >= SDAM_MEM_START && offset <= sdam_mem_end
  43                                && (offset + len - 1) <= sdam_mem_end)
  44                return true;
  45        else if ((offset == SDAM_PBS_TRIG_SET || offset == SDAM_PBS_TRIG_CLR)
  46                                && (len == 1))
  47                return true;
  48
  49        return false;
  50}
  51
  52static bool sdam_is_ro(unsigned int offset, size_t len)
  53{
  54        int i;
  55
  56        for (i = 0; i < ARRAY_SIZE(sdam_ro_map); i++)
  57                if (offset <= sdam_ro_map[i] && (offset + len) > sdam_ro_map[i])
  58                        return true;
  59
  60        return false;
  61}
  62
  63static int sdam_read(void *priv, unsigned int offset, void *val,
  64                                size_t bytes)
  65{
  66        struct sdam_chip *sdam = priv;
  67        struct device *dev = sdam->sdam_config.dev;
  68        int rc;
  69
  70        if (!sdam_is_valid(sdam, offset, bytes)) {
  71                dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
  72                        offset, bytes);
  73                return -EINVAL;
  74        }
  75
  76        rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes);
  77        if (rc < 0)
  78                dev_err(dev, "Failed to read SDAM offset %#x len=%zd, rc=%d\n",
  79                                                offset, bytes, rc);
  80
  81        return rc;
  82}
  83
  84static int sdam_write(void *priv, unsigned int offset, void *val,
  85                                size_t bytes)
  86{
  87        struct sdam_chip *sdam = priv;
  88        struct device *dev = sdam->sdam_config.dev;
  89        int rc;
  90
  91        if (!sdam_is_valid(sdam, offset, bytes)) {
  92                dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
  93                        offset, bytes);
  94                return -EINVAL;
  95        }
  96
  97        if (sdam_is_ro(offset, bytes)) {
  98                dev_err(dev, "Invalid write offset %#x len=%zd\n",
  99                        offset, bytes);
 100                return -EINVAL;
 101        }
 102
 103        rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes);
 104        if (rc < 0)
 105                dev_err(dev, "Failed to write SDAM offset %#x len=%zd, rc=%d\n",
 106                                                offset, bytes, rc);
 107
 108        return rc;
 109}
 110
 111static int sdam_probe(struct platform_device *pdev)
 112{
 113        struct sdam_chip *sdam;
 114        struct nvmem_device *nvmem;
 115        unsigned int val;
 116        int rc;
 117
 118        sdam = devm_kzalloc(&pdev->dev, sizeof(*sdam), GFP_KERNEL);
 119        if (!sdam)
 120                return -ENOMEM;
 121
 122        sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 123        if (!sdam->regmap) {
 124                dev_err(&pdev->dev, "Failed to get regmap handle\n");
 125                return -ENXIO;
 126        }
 127
 128        rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base);
 129        if (rc < 0) {
 130                dev_err(&pdev->dev, "Failed to get SDAM base, rc=%d\n", rc);
 131                return -EINVAL;
 132        }
 133
 134        rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val);
 135        if (rc < 0) {
 136                dev_err(&pdev->dev, "Failed to read SDAM_SIZE rc=%d\n", rc);
 137                return -EINVAL;
 138        }
 139        sdam->size = val * 32;
 140
 141        sdam->sdam_config.dev = &pdev->dev;
 142        sdam->sdam_config.name = "spmi_sdam";
 143        sdam->sdam_config.id = NVMEM_DEVID_AUTO;
 144        sdam->sdam_config.owner = THIS_MODULE;
 145        sdam->sdam_config.stride = 1;
 146        sdam->sdam_config.word_size = 1;
 147        sdam->sdam_config.reg_read = sdam_read;
 148        sdam->sdam_config.reg_write = sdam_write;
 149        sdam->sdam_config.priv = sdam;
 150
 151        nvmem = devm_nvmem_register(&pdev->dev, &sdam->sdam_config);
 152        if (IS_ERR(nvmem)) {
 153                dev_err(&pdev->dev,
 154                        "Failed to register SDAM nvmem device rc=%ld\n",
 155                        PTR_ERR(nvmem));
 156                return -ENXIO;
 157        }
 158        dev_dbg(&pdev->dev,
 159                "SDAM base=%#x size=%u registered successfully\n",
 160                sdam->base, sdam->size);
 161
 162        return 0;
 163}
 164
 165static const struct of_device_id sdam_match_table[] = {
 166        { .compatible = "qcom,spmi-sdam" },
 167        {},
 168};
 169
 170static struct platform_driver sdam_driver = {
 171        .driver = {
 172                .name = "qcom,spmi-sdam",
 173                .of_match_table = sdam_match_table,
 174        },
 175        .probe          = sdam_probe,
 176};
 177
 178static int __init sdam_init(void)
 179{
 180        return platform_driver_register(&sdam_driver);
 181}
 182subsys_initcall(sdam_init);
 183
 184static void __exit sdam_exit(void)
 185{
 186        return platform_driver_unregister(&sdam_driver);
 187}
 188module_exit(sdam_exit);
 189
 190MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
 191MODULE_LICENSE("GPL v2");
 192