regmap-spmi.c (5074B)
1// SPDX-License-Identifier: GPL-2.0 2// 3// Register map access API - SPMI support 4// 5// Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 6// 7// Based on regmap-i2c.c: 8// Copyright 2011 Wolfson Microelectronics plc 9// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 10 11#include <linux/regmap.h> 12#include <linux/spmi.h> 13#include <linux/module.h> 14#include <linux/init.h> 15 16static int regmap_spmi_base_read(void *context, 17 const void *reg, size_t reg_size, 18 void *val, size_t val_size) 19{ 20 u8 addr = *(u8 *)reg; 21 int err = 0; 22 23 BUG_ON(reg_size != 1); 24 25 while (val_size-- && !err) 26 err = spmi_register_read(context, addr++, val++); 27 28 return err; 29} 30 31static int regmap_spmi_base_gather_write(void *context, 32 const void *reg, size_t reg_size, 33 const void *val, size_t val_size) 34{ 35 const u8 *data = val; 36 u8 addr = *(u8 *)reg; 37 int err = 0; 38 39 BUG_ON(reg_size != 1); 40 41 /* 42 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence, 43 * use it when possible. 44 */ 45 if (addr == 0 && val_size) { 46 err = spmi_register_zero_write(context, *data); 47 if (err) 48 goto err_out; 49 50 data++; 51 addr++; 52 val_size--; 53 } 54 55 while (val_size) { 56 err = spmi_register_write(context, addr, *data); 57 if (err) 58 goto err_out; 59 60 data++; 61 addr++; 62 val_size--; 63 } 64 65err_out: 66 return err; 67} 68 69static int regmap_spmi_base_write(void *context, const void *data, 70 size_t count) 71{ 72 BUG_ON(count < 1); 73 return regmap_spmi_base_gather_write(context, data, 1, data + 1, 74 count - 1); 75} 76 77static const struct regmap_bus regmap_spmi_base = { 78 .read = regmap_spmi_base_read, 79 .write = regmap_spmi_base_write, 80 .gather_write = regmap_spmi_base_gather_write, 81 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 82 .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 83}; 84 85struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev, 86 const struct regmap_config *config, 87 struct lock_class_key *lock_key, 88 const char *lock_name) 89{ 90 return __regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, 91 lock_key, lock_name); 92} 93EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); 94 95struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev, 96 const struct regmap_config *config, 97 struct lock_class_key *lock_key, 98 const char *lock_name) 99{ 100 return __devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, 101 lock_key, lock_name); 102} 103EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base); 104 105static int regmap_spmi_ext_read(void *context, 106 const void *reg, size_t reg_size, 107 void *val, size_t val_size) 108{ 109 int err = 0; 110 size_t len; 111 u16 addr; 112 113 BUG_ON(reg_size != 2); 114 115 addr = *(u16 *)reg; 116 117 /* 118 * Split accesses into two to take advantage of the more 119 * bandwidth-efficient 'Extended Register Read' command when possible 120 */ 121 while (addr <= 0xFF && val_size) { 122 len = min_t(size_t, val_size, 16); 123 124 err = spmi_ext_register_read(context, addr, val, len); 125 if (err) 126 goto err_out; 127 128 addr += len; 129 val += len; 130 val_size -= len; 131 } 132 133 while (val_size) { 134 len = min_t(size_t, val_size, 8); 135 136 err = spmi_ext_register_readl(context, addr, val, len); 137 if (err) 138 goto err_out; 139 140 addr += len; 141 val += len; 142 val_size -= len; 143 } 144 145err_out: 146 return err; 147} 148 149static int regmap_spmi_ext_gather_write(void *context, 150 const void *reg, size_t reg_size, 151 const void *val, size_t val_size) 152{ 153 int err = 0; 154 size_t len; 155 u16 addr; 156 157 BUG_ON(reg_size != 2); 158 159 addr = *(u16 *)reg; 160 161 while (addr <= 0xFF && val_size) { 162 len = min_t(size_t, val_size, 16); 163 164 err = spmi_ext_register_write(context, addr, val, len); 165 if (err) 166 goto err_out; 167 168 addr += len; 169 val += len; 170 val_size -= len; 171 } 172 173 while (val_size) { 174 len = min_t(size_t, val_size, 8); 175 176 err = spmi_ext_register_writel(context, addr, val, len); 177 if (err) 178 goto err_out; 179 180 addr += len; 181 val += len; 182 val_size -= len; 183 } 184 185err_out: 186 return err; 187} 188 189static int regmap_spmi_ext_write(void *context, const void *data, 190 size_t count) 191{ 192 BUG_ON(count < 2); 193 return regmap_spmi_ext_gather_write(context, data, 2, data + 2, 194 count - 2); 195} 196 197static const struct regmap_bus regmap_spmi_ext = { 198 .read = regmap_spmi_ext_read, 199 .write = regmap_spmi_ext_write, 200 .gather_write = regmap_spmi_ext_gather_write, 201 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 202 .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 203}; 204 205struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev, 206 const struct regmap_config *config, 207 struct lock_class_key *lock_key, 208 const char *lock_name) 209{ 210 return __regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, 211 lock_key, lock_name); 212} 213EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); 214 215struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev, 216 const struct regmap_config *config, 217 struct lock_class_key *lock_key, 218 const char *lock_name) 219{ 220 return __devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, 221 lock_key, lock_name); 222} 223EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); 224 225MODULE_LICENSE("GPL");