1// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
2/* Copyright(c) 2014 - 2020 Intel Corporation */
3#include <linux/mutex.h>
4#include <linux/slab.h>
5#include <linux/list.h>
6#include <linux/seq_file.h>
7#include "adf_accel_devices.h"
8#include "adf_cfg.h"
9#include "adf_common_drv.h"
10
11static DEFINE_MUTEX(qat_cfg_read_lock);
12
13static void *qat_dev_cfg_start(struct seq_file *sfile, loff_t *pos)
14{
15	struct adf_cfg_device_data *dev_cfg = sfile->private;
16
17	mutex_lock(&qat_cfg_read_lock);
18	return seq_list_start(&dev_cfg->sec_list, *pos);
19}
20
21static int qat_dev_cfg_show(struct seq_file *sfile, void *v)
22{
23	struct list_head *list;
24	struct adf_cfg_section *sec =
25				list_entry(v, struct adf_cfg_section, list);
26
27	seq_printf(sfile, "[%s]\n", sec->name);
28	list_for_each(list, &sec->param_head) {
29		struct adf_cfg_key_val *ptr =
30			list_entry(list, struct adf_cfg_key_val, list);
31		seq_printf(sfile, "%s = %s\n", ptr->key, ptr->val);
32	}
33	return 0;
34}
35
36static void *qat_dev_cfg_next(struct seq_file *sfile, void *v, loff_t *pos)
37{
38	struct adf_cfg_device_data *dev_cfg = sfile->private;
39
40	return seq_list_next(v, &dev_cfg->sec_list, pos);
41}
42
43static void qat_dev_cfg_stop(struct seq_file *sfile, void *v)
44{
45	mutex_unlock(&qat_cfg_read_lock);
46}
47
48static const struct seq_operations qat_dev_cfg_sops = {
49	.start = qat_dev_cfg_start,
50	.next = qat_dev_cfg_next,
51	.stop = qat_dev_cfg_stop,
52	.show = qat_dev_cfg_show
53};
54
55DEFINE_SEQ_ATTRIBUTE(qat_dev_cfg);
56
57/**
58 * adf_cfg_dev_add() - Create an acceleration device configuration table.
59 * @accel_dev:  Pointer to acceleration device.
60 *
61 * Function creates a configuration table for the given acceleration device.
62 * The table stores device specific config values.
63 * To be used by QAT device specific drivers.
64 *
65 * Return: 0 on success, error code otherwise.
66 */
67int adf_cfg_dev_add(struct adf_accel_dev *accel_dev)
68{
69	struct adf_cfg_device_data *dev_cfg_data;
70
71	dev_cfg_data = kzalloc(sizeof(*dev_cfg_data), GFP_KERNEL);
72	if (!dev_cfg_data)
73		return -ENOMEM;
74	INIT_LIST_HEAD(&dev_cfg_data->sec_list);
75	init_rwsem(&dev_cfg_data->lock);
76	accel_dev->cfg = dev_cfg_data;
77
78	/* accel_dev->debugfs_dir should always be non-NULL here */
79	dev_cfg_data->debug = debugfs_create_file("dev_cfg", S_IRUSR,
80						  accel_dev->debugfs_dir,
81						  dev_cfg_data,
82						  &qat_dev_cfg_fops);
83	return 0;
84}
85EXPORT_SYMBOL_GPL(adf_cfg_dev_add);
86
87static void adf_cfg_section_del_all(struct list_head *head);
88
89void adf_cfg_del_all(struct adf_accel_dev *accel_dev)
90{
91	struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
92
93	down_write(&dev_cfg_data->lock);
94	adf_cfg_section_del_all(&dev_cfg_data->sec_list);
95	up_write(&dev_cfg_data->lock);
96	clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
97}
98
99/**
100 * adf_cfg_dev_remove() - Clears acceleration device configuration table.
101 * @accel_dev:  Pointer to acceleration device.
102 *
103 * Function removes configuration table from the given acceleration device
104 * and frees all allocated memory.
105 * To be used by QAT device specific drivers.
106 *
107 * Return: void
108 */
109void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev)
110{
111	struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;
112
113	if (!dev_cfg_data)
114		return;
115
116	down_write(&dev_cfg_data->lock);
117	adf_cfg_section_del_all(&dev_cfg_data->sec_list);
118	up_write(&dev_cfg_data->lock);
119	debugfs_remove(dev_cfg_data->debug);
120	kfree(dev_cfg_data);
121	accel_dev->cfg = NULL;
122}
123EXPORT_SYMBOL_GPL(adf_cfg_dev_remove);
124
125static void adf_cfg_keyval_add(struct adf_cfg_key_val *new,
126			       struct adf_cfg_section *sec)
127{
128	list_add_tail(&new->list, &sec->param_head);
129}
130
131static void adf_cfg_keyval_del_all(struct list_head *head)
132{
133	struct list_head *list_ptr, *tmp;
134
135	list_for_each_prev_safe(list_ptr, tmp, head) {
136		struct adf_cfg_key_val *ptr =
137			list_entry(list_ptr, struct adf_cfg_key_val, list);
138		list_del(list_ptr);
139		kfree(ptr);
140	}
141}
142
143static void adf_cfg_section_del_all(struct list_head *head)
144{
145	struct adf_cfg_section *ptr;
146	struct list_head *list, *tmp;
147
148	list_for_each_prev_safe(list, tmp, head) {
149		ptr = list_entry(list, struct adf_cfg_section, list);
150		adf_cfg_keyval_del_all(&ptr->param_head);
151		list_del(list);
152		kfree(ptr);
153	}
154}
155
156static struct adf_cfg_key_val *adf_cfg_key_value_find(struct adf_cfg_section *s,
157						      const char *key)
158{
159	struct list_head *list;
160
161	list_for_each(list, &s->param_head) {
162		struct adf_cfg_key_val *ptr =
163			list_entry(list, struct adf_cfg_key_val, list);
164		if (!strcmp(ptr->key, key))
165			return ptr;
166	}
167	return NULL;
168}
169
170static struct adf_cfg_section *adf_cfg_sec_find(struct adf_accel_dev *accel_dev,
171						const char *sec_name)
172{
173	struct adf_cfg_device_data *cfg = accel_dev->cfg;
174	struct list_head *list;
175
176	list_for_each(list, &cfg->sec_list) {
177		struct adf_cfg_section *ptr =
178			list_entry(list, struct adf_cfg_section, list);
179		if (!strcmp(ptr->name, sec_name))
180			return ptr;
181	}
182	return NULL;
183}
184
185static int adf_cfg_key_val_get(struct adf_accel_dev *accel_dev,
186			       const char *sec_name,
187			       const char *key_name,
188			       char *val)
189{
190	struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, sec_name);
191	struct adf_cfg_key_val *keyval = NULL;
192
193	if (sec)
194		keyval = adf_cfg_key_value_find(sec, key_name);
195	if (keyval) {
196		memcpy(val, keyval->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
197		return 0;
198	}
199	return -1;
200}
201
202/**
203 * adf_cfg_add_key_value_param() - Add key-value config entry to config table.
204 * @accel_dev:  Pointer to acceleration device.
205 * @section_name: Name of the section where the param will be added
206 * @key: The key string
207 * @val: Value pain for the given @key
208 * @type: Type - string, int or address
209 *
210 * Function adds configuration key - value entry in the appropriate section
211 * in the given acceleration device
212 * To be used by QAT device specific drivers.
213 *
214 * Return: 0 on success, error code otherwise.
215 */
216int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
217				const char *section_name,
218				const char *key, const void *val,
219				enum adf_cfg_val_type type)
220{
221	struct adf_cfg_device_data *cfg = accel_dev->cfg;
222	struct adf_cfg_key_val *key_val;
223	struct adf_cfg_section *section = adf_cfg_sec_find(accel_dev,
224							   section_name);
225	if (!section)
226		return -EFAULT;
227
228	key_val = kzalloc(sizeof(*key_val), GFP_KERNEL);
229	if (!key_val)
230		return -ENOMEM;
231
232	INIT_LIST_HEAD(&key_val->list);
233	strlcpy(key_val->key, key, sizeof(key_val->key));
234
235	if (type == ADF_DEC) {
236		snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,
237			 "%ld", (*((long *)val)));
238	} else if (type == ADF_STR) {
239		strlcpy(key_val->val, (char *)val, sizeof(key_val->val));
240	} else if (type == ADF_HEX) {
241		snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,
242			 "0x%lx", (unsigned long)val);
243	} else {
244		dev_err(&GET_DEV(accel_dev), "Unknown type given.\n");
245		kfree(key_val);
246		return -1;
247	}
248	key_val->type = type;
249	down_write(&cfg->lock);
250	adf_cfg_keyval_add(key_val, section);
251	up_write(&cfg->lock);
252	return 0;
253}
254EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param);
255
256/**
257 * adf_cfg_section_add() - Add config section entry to config table.
258 * @accel_dev:  Pointer to acceleration device.
259 * @name: Name of the section
260 *
261 * Function adds configuration section where key - value entries
262 * will be stored.
263 * To be used by QAT device specific drivers.
264 *
265 * Return: 0 on success, error code otherwise.
266 */
267int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name)
268{
269	struct adf_cfg_device_data *cfg = accel_dev->cfg;
270	struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, name);
271
272	if (sec)
273		return 0;
274
275	sec = kzalloc(sizeof(*sec), GFP_KERNEL);
276	if (!sec)
277		return -ENOMEM;
278
279	strlcpy(sec->name, name, sizeof(sec->name));
280	INIT_LIST_HEAD(&sec->param_head);
281	down_write(&cfg->lock);
282	list_add_tail(&sec->list, &cfg->sec_list);
283	up_write(&cfg->lock);
284	return 0;
285}
286EXPORT_SYMBOL_GPL(adf_cfg_section_add);
287
288int adf_cfg_get_param_value(struct adf_accel_dev *accel_dev,
289			    const char *section, const char *name,
290			    char *value)
291{
292	struct adf_cfg_device_data *cfg = accel_dev->cfg;
293	int ret;
294
295	down_read(&cfg->lock);
296	ret = adf_cfg_key_val_get(accel_dev, section, name, value);
297	up_read(&cfg->lock);
298	return ret;
299}
300