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