18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Register map access API - SPMI support
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// Based on regmap-i2c.c:
88c2ecf20Sopenharmony_ci// Copyright 2011 Wolfson Microelectronics plc
98c2ecf20Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/regmap.h>
128c2ecf20Sopenharmony_ci#include <linux/spmi.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int regmap_spmi_base_read(void *context,
178c2ecf20Sopenharmony_ci				 const void *reg, size_t reg_size,
188c2ecf20Sopenharmony_ci				 void *val, size_t val_size)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	u8 addr = *(u8 *)reg;
218c2ecf20Sopenharmony_ci	int err = 0;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	BUG_ON(reg_size != 1);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	while (val_size-- && !err)
268c2ecf20Sopenharmony_ci		err = spmi_register_read(context, addr++, val++);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return err;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int regmap_spmi_base_gather_write(void *context,
328c2ecf20Sopenharmony_ci					 const void *reg, size_t reg_size,
338c2ecf20Sopenharmony_ci					 const void *val, size_t val_size)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	const u8 *data = val;
368c2ecf20Sopenharmony_ci	u8 addr = *(u8 *)reg;
378c2ecf20Sopenharmony_ci	int err = 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	BUG_ON(reg_size != 1);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/*
428c2ecf20Sopenharmony_ci	 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
438c2ecf20Sopenharmony_ci	 * use it when possible.
448c2ecf20Sopenharmony_ci	 */
458c2ecf20Sopenharmony_ci	if (addr == 0 && val_size) {
468c2ecf20Sopenharmony_ci		err = spmi_register_zero_write(context, *data);
478c2ecf20Sopenharmony_ci		if (err)
488c2ecf20Sopenharmony_ci			goto err_out;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		data++;
518c2ecf20Sopenharmony_ci		addr++;
528c2ecf20Sopenharmony_ci		val_size--;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	while (val_size) {
568c2ecf20Sopenharmony_ci		err = spmi_register_write(context, addr, *data);
578c2ecf20Sopenharmony_ci		if (err)
588c2ecf20Sopenharmony_ci			goto err_out;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		data++;
618c2ecf20Sopenharmony_ci		addr++;
628c2ecf20Sopenharmony_ci		val_size--;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cierr_out:
668c2ecf20Sopenharmony_ci	return err;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int regmap_spmi_base_write(void *context, const void *data,
708c2ecf20Sopenharmony_ci				  size_t count)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	BUG_ON(count < 1);
738c2ecf20Sopenharmony_ci	return regmap_spmi_base_gather_write(context, data, 1, data + 1,
748c2ecf20Sopenharmony_ci					     count - 1);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic const struct regmap_bus regmap_spmi_base = {
788c2ecf20Sopenharmony_ci	.read				= regmap_spmi_base_read,
798c2ecf20Sopenharmony_ci	.write				= regmap_spmi_base_write,
808c2ecf20Sopenharmony_ci	.gather_write			= regmap_spmi_base_gather_write,
818c2ecf20Sopenharmony_ci	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
828c2ecf20Sopenharmony_ci	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct regmap *__regmap_init_spmi_base(struct spmi_device *sdev,
868c2ecf20Sopenharmony_ci				       const struct regmap_config *config,
878c2ecf20Sopenharmony_ci				       struct lock_class_key *lock_key,
888c2ecf20Sopenharmony_ci				       const char *lock_name)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	return __regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
918c2ecf20Sopenharmony_ci			     lock_key, lock_name);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_spmi_base);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev,
968c2ecf20Sopenharmony_ci					    const struct regmap_config *config,
978c2ecf20Sopenharmony_ci					    struct lock_class_key *lock_key,
988c2ecf20Sopenharmony_ci					    const char *lock_name)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	return __devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config,
1018c2ecf20Sopenharmony_ci				  lock_key, lock_name);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int regmap_spmi_ext_read(void *context,
1068c2ecf20Sopenharmony_ci				const void *reg, size_t reg_size,
1078c2ecf20Sopenharmony_ci				void *val, size_t val_size)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	int err = 0;
1108c2ecf20Sopenharmony_ci	size_t len;
1118c2ecf20Sopenharmony_ci	u16 addr;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	BUG_ON(reg_size != 2);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	addr = *(u16 *)reg;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * Split accesses into two to take advantage of the more
1198c2ecf20Sopenharmony_ci	 * bandwidth-efficient 'Extended Register Read' command when possible
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	while (addr <= 0xFF && val_size) {
1228c2ecf20Sopenharmony_ci		len = min_t(size_t, val_size, 16);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		err = spmi_ext_register_read(context, addr, val, len);
1258c2ecf20Sopenharmony_ci		if (err)
1268c2ecf20Sopenharmony_ci			goto err_out;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		addr += len;
1298c2ecf20Sopenharmony_ci		val += len;
1308c2ecf20Sopenharmony_ci		val_size -= len;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	while (val_size) {
1348c2ecf20Sopenharmony_ci		len = min_t(size_t, val_size, 8);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		err = spmi_ext_register_readl(context, addr, val, len);
1378c2ecf20Sopenharmony_ci		if (err)
1388c2ecf20Sopenharmony_ci			goto err_out;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		addr += len;
1418c2ecf20Sopenharmony_ci		val += len;
1428c2ecf20Sopenharmony_ci		val_size -= len;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cierr_out:
1468c2ecf20Sopenharmony_ci	return err;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int regmap_spmi_ext_gather_write(void *context,
1508c2ecf20Sopenharmony_ci					const void *reg, size_t reg_size,
1518c2ecf20Sopenharmony_ci					const void *val, size_t val_size)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int err = 0;
1548c2ecf20Sopenharmony_ci	size_t len;
1558c2ecf20Sopenharmony_ci	u16 addr;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	BUG_ON(reg_size != 2);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	addr = *(u16 *)reg;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	while (addr <= 0xFF && val_size) {
1628c2ecf20Sopenharmony_ci		len = min_t(size_t, val_size, 16);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		err = spmi_ext_register_write(context, addr, val, len);
1658c2ecf20Sopenharmony_ci		if (err)
1668c2ecf20Sopenharmony_ci			goto err_out;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		addr += len;
1698c2ecf20Sopenharmony_ci		val += len;
1708c2ecf20Sopenharmony_ci		val_size -= len;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	while (val_size) {
1748c2ecf20Sopenharmony_ci		len = min_t(size_t, val_size, 8);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		err = spmi_ext_register_writel(context, addr, val, len);
1778c2ecf20Sopenharmony_ci		if (err)
1788c2ecf20Sopenharmony_ci			goto err_out;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		addr += len;
1818c2ecf20Sopenharmony_ci		val += len;
1828c2ecf20Sopenharmony_ci		val_size -= len;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cierr_out:
1868c2ecf20Sopenharmony_ci	return err;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int regmap_spmi_ext_write(void *context, const void *data,
1908c2ecf20Sopenharmony_ci				 size_t count)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	BUG_ON(count < 2);
1938c2ecf20Sopenharmony_ci	return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
1948c2ecf20Sopenharmony_ci					    count - 2);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic const struct regmap_bus regmap_spmi_ext = {
1988c2ecf20Sopenharmony_ci	.read				= regmap_spmi_ext_read,
1998c2ecf20Sopenharmony_ci	.write				= regmap_spmi_ext_write,
2008c2ecf20Sopenharmony_ci	.gather_write			= regmap_spmi_ext_gather_write,
2018c2ecf20Sopenharmony_ci	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
2028c2ecf20Sopenharmony_ci	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistruct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev,
2068c2ecf20Sopenharmony_ci				      const struct regmap_config *config,
2078c2ecf20Sopenharmony_ci				      struct lock_class_key *lock_key,
2088c2ecf20Sopenharmony_ci				      const char *lock_name)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	return __regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
2118c2ecf20Sopenharmony_ci			     lock_key, lock_name);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__regmap_init_spmi_ext);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistruct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
2168c2ecf20Sopenharmony_ci					   const struct regmap_config *config,
2178c2ecf20Sopenharmony_ci					   struct lock_class_key *lock_key,
2188c2ecf20Sopenharmony_ci					   const char *lock_name)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	return __devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config,
2218c2ecf20Sopenharmony_ci				  lock_key, lock_name);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
226