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, ®map_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, ®map_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, ®map_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, ®map_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