162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Register map access API - SPMI support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// Based on regmap-i2c.c:
862306a36Sopenharmony_ci// Copyright 2011 Wolfson Microelectronics plc
962306a36Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/regmap.h>
1262306a36Sopenharmony_ci#include <linux/spmi.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int regmap_spmi_base_read(void *context,
1762306a36Sopenharmony_ci				 const void *reg, size_t reg_size,
1862306a36Sopenharmony_ci				 void *val, size_t val_size)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	u8 addr = *(u8 *)reg;
2162306a36Sopenharmony_ci	int err = 0;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	BUG_ON(reg_size != 1);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	while (val_size-- && !err)
2662306a36Sopenharmony_ci		err = spmi_register_read(context, addr++, val++);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return err;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int regmap_spmi_base_gather_write(void *context,
3262306a36Sopenharmony_ci					 const void *reg, size_t reg_size,
3362306a36Sopenharmony_ci					 const void *val, size_t val_size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	const u8 *data = val;
3662306a36Sopenharmony_ci	u8 addr = *(u8 *)reg;
3762306a36Sopenharmony_ci	int err = 0;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	BUG_ON(reg_size != 1);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
4362306a36Sopenharmony_ci	 * use it when possible.
4462306a36Sopenharmony_ci	 */
4562306a36Sopenharmony_ci	if (addr == 0 && val_size) {
4662306a36Sopenharmony_ci		err = spmi_register_zero_write(context, *data);
4762306a36Sopenharmony_ci		if (err)
4862306a36Sopenharmony_ci			goto err_out;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		data++;
5162306a36Sopenharmony_ci		addr++;
5262306a36Sopenharmony_ci		val_size--;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	while (val_size) {
5662306a36Sopenharmony_ci		err = spmi_register_write(context, addr, *data);
5762306a36Sopenharmony_ci		if (err)
5862306a36Sopenharmony_ci			goto err_out;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		data++;
6162306a36Sopenharmony_ci		addr++;
6262306a36Sopenharmony_ci		val_size--;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cierr_out:
6662306a36Sopenharmony_ci	return err;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int regmap_spmi_base_write(void *context, const void *data,
7062306a36Sopenharmony_ci				  size_t count)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	BUG_ON(count < 1);
7362306a36Sopenharmony_ci	return regmap_spmi_base_gather_write(context, data, 1, data + 1,
7462306a36Sopenharmony_ci					     count - 1);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct regmap_bus regmap_spmi_base = {
7862306a36Sopenharmony_ci	.read				= regmap_spmi_base_read,
7962306a36Sopenharmony_ci	.write				= regmap_spmi_base_write,
8062306a36Sopenharmony_ci	.gather_write			= regmap_spmi_base_gather_write,
8162306a36Sopenharmony_ci	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
8262306a36Sopenharmony_ci	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct regmap *__regmap_init_spmi_base(struct spmi_device *sdev,
8662306a36Sopenharmony_ci				       const struct regmap_config *config,
8762306a36Sopenharmony_ci				       struct lock_class_key *lock_key,
8862306a36Sopenharmony_ci				       const char *lock_name)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return __regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
9162306a36Sopenharmony_ci			     lock_key, lock_name);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_spmi_base);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev,
9662306a36Sopenharmony_ci					    const struct regmap_config *config,
9762306a36Sopenharmony_ci					    struct lock_class_key *lock_key,
9862306a36Sopenharmony_ci					    const char *lock_name)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	return __devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
10162306a36Sopenharmony_ci				  lock_key, lock_name);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int regmap_spmi_ext_read(void *context,
10662306a36Sopenharmony_ci				const void *reg, size_t reg_size,
10762306a36Sopenharmony_ci				void *val, size_t val_size)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int err = 0;
11062306a36Sopenharmony_ci	size_t len;
11162306a36Sopenharmony_ci	u16 addr;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	BUG_ON(reg_size != 2);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	addr = *(u16 *)reg;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * Split accesses into two to take advantage of the more
11962306a36Sopenharmony_ci	 * bandwidth-efficient 'Extended Register Read' command when possible
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	while (addr <= 0xFF && val_size) {
12262306a36Sopenharmony_ci		len = min_t(size_t, val_size, 16);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		err = spmi_ext_register_read(context, addr, val, len);
12562306a36Sopenharmony_ci		if (err)
12662306a36Sopenharmony_ci			goto err_out;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		addr += len;
12962306a36Sopenharmony_ci		val += len;
13062306a36Sopenharmony_ci		val_size -= len;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	while (val_size) {
13462306a36Sopenharmony_ci		len = min_t(size_t, val_size, 8);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		err = spmi_ext_register_readl(context, addr, val, len);
13762306a36Sopenharmony_ci		if (err)
13862306a36Sopenharmony_ci			goto err_out;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		addr += len;
14162306a36Sopenharmony_ci		val += len;
14262306a36Sopenharmony_ci		val_size -= len;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cierr_out:
14662306a36Sopenharmony_ci	return err;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int regmap_spmi_ext_gather_write(void *context,
15062306a36Sopenharmony_ci					const void *reg, size_t reg_size,
15162306a36Sopenharmony_ci					const void *val, size_t val_size)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int err = 0;
15462306a36Sopenharmony_ci	size_t len;
15562306a36Sopenharmony_ci	u16 addr;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	BUG_ON(reg_size != 2);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	addr = *(u16 *)reg;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	while (addr <= 0xFF && val_size) {
16262306a36Sopenharmony_ci		len = min_t(size_t, val_size, 16);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		err = spmi_ext_register_write(context, addr, val, len);
16562306a36Sopenharmony_ci		if (err)
16662306a36Sopenharmony_ci			goto err_out;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		addr += len;
16962306a36Sopenharmony_ci		val += len;
17062306a36Sopenharmony_ci		val_size -= len;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	while (val_size) {
17462306a36Sopenharmony_ci		len = min_t(size_t, val_size, 8);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		err = spmi_ext_register_writel(context, addr, val, len);
17762306a36Sopenharmony_ci		if (err)
17862306a36Sopenharmony_ci			goto err_out;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		addr += len;
18162306a36Sopenharmony_ci		val += len;
18262306a36Sopenharmony_ci		val_size -= len;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cierr_out:
18662306a36Sopenharmony_ci	return err;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int regmap_spmi_ext_write(void *context, const void *data,
19062306a36Sopenharmony_ci				 size_t count)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	BUG_ON(count < 2);
19362306a36Sopenharmony_ci	return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
19462306a36Sopenharmony_ci					    count - 2);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic const struct regmap_bus regmap_spmi_ext = {
19862306a36Sopenharmony_ci	.read				= regmap_spmi_ext_read,
19962306a36Sopenharmony_ci	.write				= regmap_spmi_ext_write,
20062306a36Sopenharmony_ci	.gather_write			= regmap_spmi_ext_gather_write,
20162306a36Sopenharmony_ci	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
20262306a36Sopenharmony_ci	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistruct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev,
20662306a36Sopenharmony_ci				      const struct regmap_config *config,
20762306a36Sopenharmony_ci				      struct lock_class_key *lock_key,
20862306a36Sopenharmony_ci				      const char *lock_name)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	return __regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
21162306a36Sopenharmony_ci			     lock_key, lock_name);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_spmi_ext);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistruct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
21662306a36Sopenharmony_ci					   const struct regmap_config *config,
21762306a36Sopenharmony_ci					   struct lock_class_key *lock_key,
21862306a36Sopenharmony_ci					   const char *lock_name)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	return __devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
22162306a36Sopenharmony_ci				  lock_key, lock_name);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
226