xref: /openbmc/linux/drivers/nvmem/qcom-spmi-sdam.c (revision 9bf75da0e2613d64c3d5e965d49fb80820d367cf)
140ce9798SAnirudh Ghayal // SPDX-License-Identifier: GPL-2.0-only
240ce9798SAnirudh Ghayal /*
3e2057ee2SSubbaraman Narayanamurthy  * Copyright (c) 2017, 2020-2021, The Linux Foundation. All rights reserved.
440ce9798SAnirudh Ghayal  */
540ce9798SAnirudh Ghayal 
640ce9798SAnirudh Ghayal #include <linux/device.h>
740ce9798SAnirudh Ghayal #include <linux/module.h>
840ce9798SAnirudh Ghayal #include <linux/of.h>
940ce9798SAnirudh Ghayal #include <linux/nvmem-provider.h>
10*9bf75da0SRob Herring #include <linux/platform_device.h>
1140ce9798SAnirudh Ghayal #include <linux/regmap.h>
1240ce9798SAnirudh Ghayal 
1340ce9798SAnirudh Ghayal #define SDAM_MEM_START			0x40
1440ce9798SAnirudh Ghayal #define REGISTER_MAP_ID			0x40
1540ce9798SAnirudh Ghayal #define REGISTER_MAP_VERSION		0x41
1640ce9798SAnirudh Ghayal #define SDAM_SIZE			0x44
1740ce9798SAnirudh Ghayal #define SDAM_PBS_TRIG_SET		0xE5
1840ce9798SAnirudh Ghayal #define SDAM_PBS_TRIG_CLR		0xE6
1940ce9798SAnirudh Ghayal 
2040ce9798SAnirudh Ghayal struct sdam_chip {
2140ce9798SAnirudh Ghayal 	struct regmap			*regmap;
2240ce9798SAnirudh Ghayal 	struct nvmem_config		sdam_config;
2340ce9798SAnirudh Ghayal 	unsigned int			base;
2440ce9798SAnirudh Ghayal 	unsigned int			size;
2540ce9798SAnirudh Ghayal };
2640ce9798SAnirudh Ghayal 
2740ce9798SAnirudh Ghayal /* read only register offsets */
2840ce9798SAnirudh Ghayal static const u8 sdam_ro_map[] = {
2940ce9798SAnirudh Ghayal 	REGISTER_MAP_ID,
3040ce9798SAnirudh Ghayal 	REGISTER_MAP_VERSION,
3140ce9798SAnirudh Ghayal 	SDAM_SIZE
3240ce9798SAnirudh Ghayal };
3340ce9798SAnirudh Ghayal 
3440ce9798SAnirudh Ghayal static bool sdam_is_valid(struct sdam_chip *sdam, unsigned int offset,
3540ce9798SAnirudh Ghayal 				size_t len)
3640ce9798SAnirudh Ghayal {
3740ce9798SAnirudh Ghayal 	unsigned int sdam_mem_end = SDAM_MEM_START + sdam->size - 1;
3840ce9798SAnirudh Ghayal 
3940ce9798SAnirudh Ghayal 	if (!len)
4040ce9798SAnirudh Ghayal 		return false;
4140ce9798SAnirudh Ghayal 
4240ce9798SAnirudh Ghayal 	if (offset >= SDAM_MEM_START && offset <= sdam_mem_end
4340ce9798SAnirudh Ghayal 				&& (offset + len - 1) <= sdam_mem_end)
4440ce9798SAnirudh Ghayal 		return true;
4540ce9798SAnirudh Ghayal 	else if ((offset == SDAM_PBS_TRIG_SET || offset == SDAM_PBS_TRIG_CLR)
4640ce9798SAnirudh Ghayal 				&& (len == 1))
4740ce9798SAnirudh Ghayal 		return true;
4840ce9798SAnirudh Ghayal 
4940ce9798SAnirudh Ghayal 	return false;
5040ce9798SAnirudh Ghayal }
5140ce9798SAnirudh Ghayal 
5240ce9798SAnirudh Ghayal static bool sdam_is_ro(unsigned int offset, size_t len)
5340ce9798SAnirudh Ghayal {
5440ce9798SAnirudh Ghayal 	int i;
5540ce9798SAnirudh Ghayal 
5640ce9798SAnirudh Ghayal 	for (i = 0; i < ARRAY_SIZE(sdam_ro_map); i++)
5740ce9798SAnirudh Ghayal 		if (offset <= sdam_ro_map[i] && (offset + len) > sdam_ro_map[i])
5840ce9798SAnirudh Ghayal 			return true;
5940ce9798SAnirudh Ghayal 
6040ce9798SAnirudh Ghayal 	return false;
6140ce9798SAnirudh Ghayal }
6240ce9798SAnirudh Ghayal 
6340ce9798SAnirudh Ghayal static int sdam_read(void *priv, unsigned int offset, void *val,
6440ce9798SAnirudh Ghayal 				size_t bytes)
6540ce9798SAnirudh Ghayal {
6640ce9798SAnirudh Ghayal 	struct sdam_chip *sdam = priv;
67e2057ee2SSubbaraman Narayanamurthy 	struct device *dev = sdam->sdam_config.dev;
6840ce9798SAnirudh Ghayal 	int rc;
6940ce9798SAnirudh Ghayal 
7040ce9798SAnirudh Ghayal 	if (!sdam_is_valid(sdam, offset, bytes)) {
7140ce9798SAnirudh Ghayal 		dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
7240ce9798SAnirudh Ghayal 			offset, bytes);
7340ce9798SAnirudh Ghayal 		return -EINVAL;
7440ce9798SAnirudh Ghayal 	}
7540ce9798SAnirudh Ghayal 
7640ce9798SAnirudh Ghayal 	rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes);
7740ce9798SAnirudh Ghayal 	if (rc < 0)
7840ce9798SAnirudh Ghayal 		dev_err(dev, "Failed to read SDAM offset %#x len=%zd, rc=%d\n",
7940ce9798SAnirudh Ghayal 						offset, bytes, rc);
8040ce9798SAnirudh Ghayal 
8140ce9798SAnirudh Ghayal 	return rc;
8240ce9798SAnirudh Ghayal }
8340ce9798SAnirudh Ghayal 
8440ce9798SAnirudh Ghayal static int sdam_write(void *priv, unsigned int offset, void *val,
8540ce9798SAnirudh Ghayal 				size_t bytes)
8640ce9798SAnirudh Ghayal {
8740ce9798SAnirudh Ghayal 	struct sdam_chip *sdam = priv;
88e2057ee2SSubbaraman Narayanamurthy 	struct device *dev = sdam->sdam_config.dev;
8940ce9798SAnirudh Ghayal 	int rc;
9040ce9798SAnirudh Ghayal 
9140ce9798SAnirudh Ghayal 	if (!sdam_is_valid(sdam, offset, bytes)) {
9240ce9798SAnirudh Ghayal 		dev_err(dev, "Invalid SDAM offset %#x len=%zd\n",
9340ce9798SAnirudh Ghayal 			offset, bytes);
9440ce9798SAnirudh Ghayal 		return -EINVAL;
9540ce9798SAnirudh Ghayal 	}
9640ce9798SAnirudh Ghayal 
9740ce9798SAnirudh Ghayal 	if (sdam_is_ro(offset, bytes)) {
9840ce9798SAnirudh Ghayal 		dev_err(dev, "Invalid write offset %#x len=%zd\n",
9940ce9798SAnirudh Ghayal 			offset, bytes);
10040ce9798SAnirudh Ghayal 		return -EINVAL;
10140ce9798SAnirudh Ghayal 	}
10240ce9798SAnirudh Ghayal 
10340ce9798SAnirudh Ghayal 	rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes);
10440ce9798SAnirudh Ghayal 	if (rc < 0)
10540ce9798SAnirudh Ghayal 		dev_err(dev, "Failed to write SDAM offset %#x len=%zd, rc=%d\n",
10640ce9798SAnirudh Ghayal 						offset, bytes, rc);
10740ce9798SAnirudh Ghayal 
10840ce9798SAnirudh Ghayal 	return rc;
10940ce9798SAnirudh Ghayal }
11040ce9798SAnirudh Ghayal 
11140ce9798SAnirudh Ghayal static int sdam_probe(struct platform_device *pdev)
11240ce9798SAnirudh Ghayal {
11340ce9798SAnirudh Ghayal 	struct sdam_chip *sdam;
11440ce9798SAnirudh Ghayal 	struct nvmem_device *nvmem;
11540ce9798SAnirudh Ghayal 	unsigned int val;
11640ce9798SAnirudh Ghayal 	int rc;
11740ce9798SAnirudh Ghayal 
11840ce9798SAnirudh Ghayal 	sdam = devm_kzalloc(&pdev->dev, sizeof(*sdam), GFP_KERNEL);
11940ce9798SAnirudh Ghayal 	if (!sdam)
12040ce9798SAnirudh Ghayal 		return -ENOMEM;
12140ce9798SAnirudh Ghayal 
12240ce9798SAnirudh Ghayal 	sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL);
12340ce9798SAnirudh Ghayal 	if (!sdam->regmap) {
12440ce9798SAnirudh Ghayal 		dev_err(&pdev->dev, "Failed to get regmap handle\n");
12540ce9798SAnirudh Ghayal 		return -ENXIO;
12640ce9798SAnirudh Ghayal 	}
12740ce9798SAnirudh Ghayal 
12840ce9798SAnirudh Ghayal 	rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base);
12940ce9798SAnirudh Ghayal 	if (rc < 0) {
13040ce9798SAnirudh Ghayal 		dev_err(&pdev->dev, "Failed to get SDAM base, rc=%d\n", rc);
13140ce9798SAnirudh Ghayal 		return -EINVAL;
13240ce9798SAnirudh Ghayal 	}
13340ce9798SAnirudh Ghayal 
13440ce9798SAnirudh Ghayal 	rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val);
13540ce9798SAnirudh Ghayal 	if (rc < 0) {
13640ce9798SAnirudh Ghayal 		dev_err(&pdev->dev, "Failed to read SDAM_SIZE rc=%d\n", rc);
13740ce9798SAnirudh Ghayal 		return -EINVAL;
13840ce9798SAnirudh Ghayal 	}
13940ce9798SAnirudh Ghayal 	sdam->size = val * 32;
14040ce9798SAnirudh Ghayal 
14140ce9798SAnirudh Ghayal 	sdam->sdam_config.dev = &pdev->dev;
14240ce9798SAnirudh Ghayal 	sdam->sdam_config.name = "spmi_sdam";
1438f042191SGuru Das Srinagesh 	sdam->sdam_config.id = NVMEM_DEVID_AUTO;
144e050f160SZheng Yongjun 	sdam->sdam_config.owner = THIS_MODULE;
14540ce9798SAnirudh Ghayal 	sdam->sdam_config.stride = 1;
14640ce9798SAnirudh Ghayal 	sdam->sdam_config.word_size = 1;
14740ce9798SAnirudh Ghayal 	sdam->sdam_config.reg_read = sdam_read;
14840ce9798SAnirudh Ghayal 	sdam->sdam_config.reg_write = sdam_write;
14940ce9798SAnirudh Ghayal 	sdam->sdam_config.priv = sdam;
15040ce9798SAnirudh Ghayal 
15140ce9798SAnirudh Ghayal 	nvmem = devm_nvmem_register(&pdev->dev, &sdam->sdam_config);
15240ce9798SAnirudh Ghayal 	if (IS_ERR(nvmem)) {
15340ce9798SAnirudh Ghayal 		dev_err(&pdev->dev,
15440ce9798SAnirudh Ghayal 			"Failed to register SDAM nvmem device rc=%ld\n",
15540ce9798SAnirudh Ghayal 			PTR_ERR(nvmem));
15640ce9798SAnirudh Ghayal 		return -ENXIO;
15740ce9798SAnirudh Ghayal 	}
15840ce9798SAnirudh Ghayal 	dev_dbg(&pdev->dev,
15940ce9798SAnirudh Ghayal 		"SDAM base=%#x size=%u registered successfully\n",
16040ce9798SAnirudh Ghayal 		sdam->base, sdam->size);
16140ce9798SAnirudh Ghayal 
16240ce9798SAnirudh Ghayal 	return 0;
16340ce9798SAnirudh Ghayal }
16440ce9798SAnirudh Ghayal 
16540ce9798SAnirudh Ghayal static const struct of_device_id sdam_match_table[] = {
16640ce9798SAnirudh Ghayal 	{ .compatible = "qcom,spmi-sdam" },
16740ce9798SAnirudh Ghayal 	{},
16840ce9798SAnirudh Ghayal };
1691ca7fca3SJohan Hovold MODULE_DEVICE_TABLE(of, sdam_match_table);
17040ce9798SAnirudh Ghayal 
17140ce9798SAnirudh Ghayal static struct platform_driver sdam_driver = {
17240ce9798SAnirudh Ghayal 	.driver = {
17340ce9798SAnirudh Ghayal 		.name = "qcom,spmi-sdam",
17440ce9798SAnirudh Ghayal 		.of_match_table = sdam_match_table,
17540ce9798SAnirudh Ghayal 	},
17640ce9798SAnirudh Ghayal 	.probe		= sdam_probe,
17740ce9798SAnirudh Ghayal };
178eb7dda20SJohan Hovold module_platform_driver(sdam_driver);
17940ce9798SAnirudh Ghayal 
18040ce9798SAnirudh Ghayal MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
18140ce9798SAnirudh Ghayal MODULE_LICENSE("GPL v2");
182