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 16 static 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 31 static 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 65 err_out: 66 return err; 67 } 68 69 static 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 77 static 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 85 struct 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 } 93 EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); 94 95 struct 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 } 103 EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base); 104 105 static 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 145 err_out: 146 return err; 147 } 148 149 static 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 185 err_out: 186 return err; 187 } 188 189 static 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 197 static 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 205 struct 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 } 213 EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); 214 215 struct 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 } 223 EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); 224 225 MODULE_LICENSE("GPL"); 226