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