1// SPDX-License-Identifier: GPL-2.0
2//
3// Register map access API - FSI support
4//
5// Copyright 2022 IBM Corp
6//
7// Author: Eddie James <eajames@linux.ibm.com>
8
9#include <linux/fsi.h>
10#include <linux/module.h>
11#include <linux/regmap.h>
12
13#include "internal.h"
14
15static int regmap_fsi32_reg_read(void *context, unsigned int reg, unsigned int *val)
16{
17	u32 v;
18	int ret;
19
20	ret = fsi_slave_read(context, reg, &v, sizeof(v));
21	if (ret)
22		return ret;
23
24	*val = v;
25	return 0;
26}
27
28static int regmap_fsi32_reg_write(void *context, unsigned int reg, unsigned int val)
29{
30	u32 v = val;
31
32	return fsi_slave_write(context, reg, &v, sizeof(v));
33}
34
35static const struct regmap_bus regmap_fsi32 = {
36	.reg_write = regmap_fsi32_reg_write,
37	.reg_read = regmap_fsi32_reg_read,
38};
39
40static int regmap_fsi32le_reg_read(void *context, unsigned int reg, unsigned int *val)
41{
42	__be32 v;
43	int ret;
44
45	ret = fsi_slave_read(context, reg, &v, sizeof(v));
46	if (ret)
47		return ret;
48
49	*val = be32_to_cpu(v);
50	return 0;
51}
52
53static int regmap_fsi32le_reg_write(void *context, unsigned int reg, unsigned int val)
54{
55	__be32 v = cpu_to_be32(val);
56
57	return fsi_slave_write(context, reg, &v, sizeof(v));
58}
59
60static const struct regmap_bus regmap_fsi32le = {
61	.reg_write = regmap_fsi32le_reg_write,
62	.reg_read = regmap_fsi32le_reg_read,
63};
64
65static int regmap_fsi16_reg_read(void *context, unsigned int reg, unsigned int *val)
66{
67	u16 v;
68	int ret;
69
70	ret = fsi_slave_read(context, reg, &v, sizeof(v));
71	if (ret)
72		return ret;
73
74	*val = v;
75	return 0;
76}
77
78static int regmap_fsi16_reg_write(void *context, unsigned int reg, unsigned int val)
79{
80	u16 v;
81
82	if (val > 0xffff)
83		return -EINVAL;
84
85	v = val;
86	return fsi_slave_write(context, reg, &v, sizeof(v));
87}
88
89static const struct regmap_bus regmap_fsi16 = {
90	.reg_write = regmap_fsi16_reg_write,
91	.reg_read = regmap_fsi16_reg_read,
92};
93
94static int regmap_fsi16le_reg_read(void *context, unsigned int reg, unsigned int *val)
95{
96	__be16 v;
97	int ret;
98
99	ret = fsi_slave_read(context, reg, &v, sizeof(v));
100	if (ret)
101		return ret;
102
103	*val = be16_to_cpu(v);
104	return 0;
105}
106
107static int regmap_fsi16le_reg_write(void *context, unsigned int reg, unsigned int val)
108{
109	__be16 v;
110
111	if (val > 0xffff)
112		return -EINVAL;
113
114	v = cpu_to_be16(val);
115	return fsi_slave_write(context, reg, &v, sizeof(v));
116}
117
118static const struct regmap_bus regmap_fsi16le = {
119	.reg_write = regmap_fsi16le_reg_write,
120	.reg_read = regmap_fsi16le_reg_read,
121};
122
123static int regmap_fsi8_reg_read(void *context, unsigned int reg, unsigned int *val)
124{
125	u8 v;
126	int ret;
127
128	ret = fsi_slave_read(context, reg, &v, sizeof(v));
129	if (ret)
130		return ret;
131
132	*val = v;
133	return 0;
134}
135
136static int regmap_fsi8_reg_write(void *context, unsigned int reg, unsigned int val)
137{
138	u8 v;
139
140	if (val > 0xff)
141		return -EINVAL;
142
143	v = val;
144	return fsi_slave_write(context, reg, &v, sizeof(v));
145}
146
147static const struct regmap_bus regmap_fsi8 = {
148	.reg_write = regmap_fsi8_reg_write,
149	.reg_read = regmap_fsi8_reg_read,
150};
151
152static const struct regmap_bus *regmap_get_fsi_bus(struct fsi_device *fsi_dev,
153						   const struct regmap_config *config)
154{
155	const struct regmap_bus *bus = NULL;
156
157	if (config->reg_bits == 8 || config->reg_bits == 16 || config->reg_bits == 32) {
158		switch (config->val_bits) {
159		case 8:
160			bus = &regmap_fsi8;
161			break;
162		case 16:
163			switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
164			case REGMAP_ENDIAN_LITTLE:
165#ifdef __LITTLE_ENDIAN
166			case REGMAP_ENDIAN_NATIVE:
167#endif
168				bus = &regmap_fsi16le;
169				break;
170			case REGMAP_ENDIAN_DEFAULT:
171			case REGMAP_ENDIAN_BIG:
172#ifdef __BIG_ENDIAN
173			case REGMAP_ENDIAN_NATIVE:
174#endif
175				bus = &regmap_fsi16;
176				break;
177			default:
178				break;
179			}
180			break;
181		case 32:
182			switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
183			case REGMAP_ENDIAN_LITTLE:
184#ifdef __LITTLE_ENDIAN
185			case REGMAP_ENDIAN_NATIVE:
186#endif
187				bus = &regmap_fsi32le;
188				break;
189			case REGMAP_ENDIAN_DEFAULT:
190			case REGMAP_ENDIAN_BIG:
191#ifdef __BIG_ENDIAN
192			case REGMAP_ENDIAN_NATIVE:
193#endif
194				bus = &regmap_fsi32;
195				break;
196			default:
197				break;
198			}
199			break;
200		}
201	}
202
203	return bus ?: ERR_PTR(-EOPNOTSUPP);
204}
205
206struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config,
207				 struct lock_class_key *lock_key, const char *lock_name)
208{
209	const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
210
211	if (IS_ERR(bus))
212		return ERR_CAST(bus);
213
214	return __regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
215}
216EXPORT_SYMBOL_GPL(__regmap_init_fsi);
217
218struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,
219				      const struct regmap_config *config,
220				      struct lock_class_key *lock_key, const char *lock_name)
221{
222	const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
223
224	if (IS_ERR(bus))
225		return ERR_CAST(bus);
226
227	return __devm_regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
228}
229EXPORT_SYMBOL_GPL(__devm_regmap_init_fsi);
230
231MODULE_LICENSE("GPL");
232