xref: /linux/drivers/nvmem/max77759-nvmem.c (revision cbae17630954cb89f2bdf5bbadfd3aa81ea67283)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright 2020 Google Inc
4 // Copyright 2025 Linaro Ltd.
5 //
6 // NVMEM driver for Maxim MAX77759
7 
8 #include <linux/dev_printk.h>
9 #include <linux/device.h>
10 #include <linux/device/driver.h>
11 #include <linux/err.h>
12 #include <linux/mfd/max77759.h>
13 #include <linux/module.h>
14 #include <linux/nvmem-provider.h>
15 #include <linux/overflow.h>
16 #include <linux/platform_device.h>
17 #include <linux/string.h>
18 
19 #define MAX77759_NVMEM_OPCODE_HEADER_LEN 3
20 /*
21  * NVMEM commands have a three byte header (which becomes part of the command),
22  * so we need to subtract that.
23  */
24 #define MAX77759_NVMEM_SIZE (MAX77759_MAXQ_OPCODE_MAXLENGTH \
25 			     - MAX77759_NVMEM_OPCODE_HEADER_LEN)
26 
27 struct max77759_nvmem {
28 	struct device *dev;
29 	struct max77759 *max77759;
30 };
31 
32 static int max77759_nvmem_reg_read(void *priv, unsigned int offset,
33 				   void *val, size_t bytes)
34 {
35 	struct max77759_nvmem *nvmem = priv;
36 	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length,
37 		    MAX77759_NVMEM_OPCODE_HEADER_LEN);
38 	DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length,
39 		    MAX77759_MAXQ_OPCODE_MAXLENGTH);
40 	int ret;
41 
42 	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_USER_SPACE_READ;
43 	cmd->cmd[1] = offset;
44 	cmd->cmd[2] = bytes;
45 	rsp->length = bytes + MAX77759_NVMEM_OPCODE_HEADER_LEN;
46 
47 	ret = max77759_maxq_command(nvmem->max77759, cmd, rsp);
48 	if (ret < 0)
49 		return ret;
50 
51 	if (memcmp(cmd->cmd, rsp->rsp, MAX77759_NVMEM_OPCODE_HEADER_LEN)) {
52 		dev_warn(nvmem->dev, "protocol error (read)\n");
53 		return -EIO;
54 	}
55 
56 	memcpy(val, &rsp->rsp[MAX77759_NVMEM_OPCODE_HEADER_LEN], bytes);
57 
58 	return 0;
59 }
60 
61 static int max77759_nvmem_reg_write(void *priv, unsigned int offset,
62 				    void *val, size_t bytes)
63 {
64 	struct max77759_nvmem *nvmem = priv;
65 	DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length,
66 		    MAX77759_MAXQ_OPCODE_MAXLENGTH);
67 	DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length,
68 		    MAX77759_MAXQ_OPCODE_MAXLENGTH);
69 	int ret;
70 
71 	cmd->cmd[0] = MAX77759_MAXQ_OPCODE_USER_SPACE_WRITE;
72 	cmd->cmd[1] = offset;
73 	cmd->cmd[2] = bytes;
74 	memcpy(&cmd->cmd[MAX77759_NVMEM_OPCODE_HEADER_LEN], val, bytes);
75 	cmd->length = bytes + MAX77759_NVMEM_OPCODE_HEADER_LEN;
76 	rsp->length = cmd->length;
77 
78 	ret = max77759_maxq_command(nvmem->max77759, cmd, rsp);
79 	if (ret < 0)
80 		return ret;
81 
82 	if (memcmp(cmd->cmd, rsp->rsp, cmd->length)) {
83 		dev_warn(nvmem->dev, "protocol error (write)\n");
84 		return -EIO;
85 	}
86 
87 	return 0;
88 }
89 
90 static int max77759_nvmem_probe(struct platform_device *pdev)
91 {
92 	struct nvmem_config config = {
93 		.dev = &pdev->dev,
94 		.name = dev_name(&pdev->dev),
95 		.id = NVMEM_DEVID_NONE,
96 		.type = NVMEM_TYPE_EEPROM,
97 		.ignore_wp = true,
98 		.size = MAX77759_NVMEM_SIZE,
99 		.word_size = sizeof(u8),
100 		.stride = sizeof(u8),
101 		.reg_read = max77759_nvmem_reg_read,
102 		.reg_write = max77759_nvmem_reg_write,
103 	};
104 	struct max77759_nvmem *nvmem;
105 
106 	nvmem = devm_kzalloc(&pdev->dev, sizeof(*nvmem), GFP_KERNEL);
107 	if (!nvmem)
108 		return -ENOMEM;
109 
110 	nvmem->dev = &pdev->dev;
111 	nvmem->max77759 = dev_get_drvdata(pdev->dev.parent);
112 
113 	config.priv = nvmem;
114 
115 	return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config));
116 }
117 
118 static const struct of_device_id max77759_nvmem_of_id[] = {
119 	{ .compatible = "maxim,max77759-nvmem", },
120 	{ }
121 };
122 MODULE_DEVICE_TABLE(of, max77759_nvmem_of_id);
123 
124 static const struct platform_device_id max77759_nvmem_platform_id[] = {
125 	{ "max77759-nvmem", },
126 	{ }
127 };
128 MODULE_DEVICE_TABLE(platform, max77759_nvmem_platform_id);
129 
130 static struct platform_driver max77759_nvmem_driver = {
131 	.driver = {
132 		.name = "max77759-nvmem",
133 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
134 		.of_match_table = max77759_nvmem_of_id,
135 	},
136 	.probe = max77759_nvmem_probe,
137 	.id_table = max77759_nvmem_platform_id,
138 };
139 
140 module_platform_driver(max77759_nvmem_driver);
141 
142 MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
143 MODULE_DESCRIPTION("NVMEM driver for Maxim MAX77759");
144 MODULE_LICENSE("GPL");
145