162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Register map access API - Memory region with raw access
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// This is intended for testing only
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// Copyright (c) 2023, Arm Ltd
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/swab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "internal.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic unsigned int decode_reg(enum regmap_endian endian, const void *reg)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	const u16 *r = reg;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (endian == REGMAP_ENDIAN_BIG)
2462306a36Sopenharmony_ci		return be16_to_cpu(*r);
2562306a36Sopenharmony_ci	else
2662306a36Sopenharmony_ci		return le16_to_cpu(*r);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int regmap_raw_ram_gather_write(void *context,
3062306a36Sopenharmony_ci				       const void *reg, size_t reg_len,
3162306a36Sopenharmony_ci				       const void *val, size_t val_len)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct regmap_ram_data *data = context;
3462306a36Sopenharmony_ci	unsigned int r;
3562306a36Sopenharmony_ci	u16 *our_buf = (u16 *)data->vals;
3662306a36Sopenharmony_ci	int i;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (reg_len != 2)
3962306a36Sopenharmony_ci		return -EINVAL;
4062306a36Sopenharmony_ci	if (val_len % 2)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	r = decode_reg(data->reg_endian, reg);
4462306a36Sopenharmony_ci	memcpy(&our_buf[r], val, val_len);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	for (i = 0; i < val_len / 2; i++)
4762306a36Sopenharmony_ci		data->written[r + i] = true;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int regmap_raw_ram_write(void *context, const void *data, size_t count)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return regmap_raw_ram_gather_write(context, data, 2,
5562306a36Sopenharmony_ci					   data + 2, count - 2);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int regmap_raw_ram_read(void *context,
5962306a36Sopenharmony_ci			       const void *reg, size_t reg_len,
6062306a36Sopenharmony_ci			       void *val, size_t val_len)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct regmap_ram_data *data = context;
6362306a36Sopenharmony_ci	unsigned int r;
6462306a36Sopenharmony_ci	u16 *our_buf = (u16 *)data->vals;
6562306a36Sopenharmony_ci	int i;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (reg_len != 2)
6862306a36Sopenharmony_ci		return -EINVAL;
6962306a36Sopenharmony_ci	if (val_len % 2)
7062306a36Sopenharmony_ci		return -EINVAL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	r = decode_reg(data->reg_endian, reg);
7362306a36Sopenharmony_ci	memcpy(val, &our_buf[r], val_len);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (i = 0; i < val_len / 2; i++)
7662306a36Sopenharmony_ci		data->read[r + i] = true;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void regmap_raw_ram_free_context(void *context)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct regmap_ram_data *data = context;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	kfree(data->vals);
8662306a36Sopenharmony_ci	kfree(data->read);
8762306a36Sopenharmony_ci	kfree(data->written);
8862306a36Sopenharmony_ci	kfree(data);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct regmap_bus regmap_raw_ram = {
9262306a36Sopenharmony_ci	.fast_io = true,
9362306a36Sopenharmony_ci	.write = regmap_raw_ram_write,
9462306a36Sopenharmony_ci	.gather_write = regmap_raw_ram_gather_write,
9562306a36Sopenharmony_ci	.read = regmap_raw_ram_read,
9662306a36Sopenharmony_ci	.free_context = regmap_raw_ram_free_context,
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct regmap *__regmap_init_raw_ram(const struct regmap_config *config,
10062306a36Sopenharmony_ci				     struct regmap_ram_data *data,
10162306a36Sopenharmony_ci				     struct lock_class_key *lock_key,
10262306a36Sopenharmony_ci				     const char *lock_name)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct regmap *map;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (config->reg_bits != 16)
10762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (!config->max_register) {
11062306a36Sopenharmony_ci		pr_crit("No max_register specified for RAM regmap\n");
11162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	data->read = kcalloc(sizeof(bool), config->max_register + 1,
11562306a36Sopenharmony_ci			     GFP_KERNEL);
11662306a36Sopenharmony_ci	if (!data->read)
11762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	data->written = kcalloc(sizeof(bool), config->max_register + 1,
12062306a36Sopenharmony_ci				GFP_KERNEL);
12162306a36Sopenharmony_ci	if (!data->written)
12262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	data->reg_endian = config->reg_format_endian;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	map = __regmap_init(NULL, &regmap_raw_ram, data, config,
12762306a36Sopenharmony_ci			    lock_key, lock_name);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return map;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
134