1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Register map access API - MMIO support 4 // 5 // Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 6 7 #include <linux/clk.h> 8 #include <linux/err.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/regmap.h> 12 #include <linux/slab.h> 13 14 #include "internal.h" 15 16 struct regmap_mmio_context { 17 void __iomem *regs; 18 unsigned val_bytes; 19 20 bool attached_clk; 21 struct clk *clk; 22 23 void (*reg_write)(struct regmap_mmio_context *ctx, 24 unsigned int reg, unsigned int val); 25 unsigned int (*reg_read)(struct regmap_mmio_context *ctx, 26 unsigned int reg); 27 }; 28 29 static int regmap_mmio_regbits_check(size_t reg_bits) 30 { 31 switch (reg_bits) { 32 case 8: 33 case 16: 34 case 32: 35 #ifdef CONFIG_64BIT 36 case 64: 37 #endif 38 return 0; 39 default: 40 return -EINVAL; 41 } 42 } 43 44 static int regmap_mmio_get_min_stride(size_t val_bits) 45 { 46 int min_stride; 47 48 switch (val_bits) { 49 case 8: 50 /* The core treats 0 as 1 */ 51 min_stride = 0; 52 return 0; 53 case 16: 54 min_stride = 2; 55 break; 56 case 32: 57 min_stride = 4; 58 break; 59 #ifdef CONFIG_64BIT 60 case 64: 61 min_stride = 8; 62 break; 63 #endif 64 default: 65 return -EINVAL; 66 } 67 68 return min_stride; 69 } 70 71 static void regmap_mmio_write8(struct regmap_mmio_context *ctx, 72 unsigned int reg, 73 unsigned int val) 74 { 75 writeb(val, ctx->regs + reg); 76 } 77 78 static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, 79 unsigned int reg, 80 unsigned int val) 81 { 82 writew(val, ctx->regs + reg); 83 } 84 85 static void regmap_mmio_write16be(struct regmap_mmio_context *ctx, 86 unsigned int reg, 87 unsigned int val) 88 { 89 iowrite16be(val, ctx->regs + reg); 90 } 91 92 static void regmap_mmio_write32le(struct regmap_mmio_context *ctx, 93 unsigned int reg, 94 unsigned int val) 95 { 96 writel(val, ctx->regs + reg); 97 } 98 99 static void regmap_mmio_write32be(struct regmap_mmio_context *ctx, 100 unsigned int reg, 101 unsigned int val) 102 { 103 iowrite32be(val, ctx->regs + reg); 104 } 105 106 #ifdef CONFIG_64BIT 107 static void regmap_mmio_write64le(struct regmap_mmio_context *ctx, 108 unsigned int reg, 109 unsigned int val) 110 { 111 writeq(val, ctx->regs + reg); 112 } 113 #endif 114 115 static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val) 116 { 117 struct regmap_mmio_context *ctx = context; 118 int ret; 119 120 if (!IS_ERR(ctx->clk)) { 121 ret = clk_enable(ctx->clk); 122 if (ret < 0) 123 return ret; 124 } 125 126 ctx->reg_write(ctx, reg, val); 127 128 if (!IS_ERR(ctx->clk)) 129 clk_disable(ctx->clk); 130 131 return 0; 132 } 133 134 static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, 135 unsigned int reg) 136 { 137 return readb(ctx->regs + reg); 138 } 139 140 static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, 141 unsigned int reg) 142 { 143 return readw(ctx->regs + reg); 144 } 145 146 static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, 147 unsigned int reg) 148 { 149 return ioread16be(ctx->regs + reg); 150 } 151 152 static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, 153 unsigned int reg) 154 { 155 return readl(ctx->regs + reg); 156 } 157 158 static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx, 159 unsigned int reg) 160 { 161 return ioread32be(ctx->regs + reg); 162 } 163 164 #ifdef CONFIG_64BIT 165 static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx, 166 unsigned int reg) 167 { 168 return readq(ctx->regs + reg); 169 } 170 #endif 171 172 static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val) 173 { 174 struct regmap_mmio_context *ctx = context; 175 int ret; 176 177 if (!IS_ERR(ctx->clk)) { 178 ret = clk_enable(ctx->clk); 179 if (ret < 0) 180 return ret; 181 } 182 183 *val = ctx->reg_read(ctx, reg); 184 185 if (!IS_ERR(ctx->clk)) 186 clk_disable(ctx->clk); 187 188 return 0; 189 } 190 191 static void regmap_mmio_free_context(void *context) 192 { 193 struct regmap_mmio_context *ctx = context; 194 195 if (!IS_ERR(ctx->clk)) { 196 clk_unprepare(ctx->clk); 197 if (!ctx->attached_clk) 198 clk_put(ctx->clk); 199 } 200 kfree(context); 201 } 202 203 static const struct regmap_bus regmap_mmio = { 204 .fast_io = true, 205 .reg_write = regmap_mmio_write, 206 .reg_read = regmap_mmio_read, 207 .free_context = regmap_mmio_free_context, 208 .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 209 }; 210 211 static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 212 const char *clk_id, 213 void __iomem *regs, 214 const struct regmap_config *config) 215 { 216 struct regmap_mmio_context *ctx; 217 int min_stride; 218 int ret; 219 220 ret = regmap_mmio_regbits_check(config->reg_bits); 221 if (ret) 222 return ERR_PTR(ret); 223 224 if (config->pad_bits) 225 return ERR_PTR(-EINVAL); 226 227 min_stride = regmap_mmio_get_min_stride(config->val_bits); 228 if (min_stride < 0) 229 return ERR_PTR(min_stride); 230 231 if (config->reg_stride < min_stride) 232 return ERR_PTR(-EINVAL); 233 234 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 235 if (!ctx) 236 return ERR_PTR(-ENOMEM); 237 238 ctx->regs = regs; 239 ctx->val_bytes = config->val_bits / 8; 240 ctx->clk = ERR_PTR(-ENODEV); 241 242 switch (regmap_get_val_endian(dev, ®map_mmio, config)) { 243 case REGMAP_ENDIAN_DEFAULT: 244 case REGMAP_ENDIAN_LITTLE: 245 #ifdef __LITTLE_ENDIAN 246 case REGMAP_ENDIAN_NATIVE: 247 #endif 248 switch (config->val_bits) { 249 case 8: 250 ctx->reg_read = regmap_mmio_read8; 251 ctx->reg_write = regmap_mmio_write8; 252 break; 253 case 16: 254 ctx->reg_read = regmap_mmio_read16le; 255 ctx->reg_write = regmap_mmio_write16le; 256 break; 257 case 32: 258 ctx->reg_read = regmap_mmio_read32le; 259 ctx->reg_write = regmap_mmio_write32le; 260 break; 261 #ifdef CONFIG_64BIT 262 case 64: 263 ctx->reg_read = regmap_mmio_read64le; 264 ctx->reg_write = regmap_mmio_write64le; 265 break; 266 #endif 267 default: 268 ret = -EINVAL; 269 goto err_free; 270 } 271 break; 272 case REGMAP_ENDIAN_BIG: 273 #ifdef __BIG_ENDIAN 274 case REGMAP_ENDIAN_NATIVE: 275 #endif 276 switch (config->val_bits) { 277 case 8: 278 ctx->reg_read = regmap_mmio_read8; 279 ctx->reg_write = regmap_mmio_write8; 280 break; 281 case 16: 282 ctx->reg_read = regmap_mmio_read16be; 283 ctx->reg_write = regmap_mmio_write16be; 284 break; 285 case 32: 286 ctx->reg_read = regmap_mmio_read32be; 287 ctx->reg_write = regmap_mmio_write32be; 288 break; 289 default: 290 ret = -EINVAL; 291 goto err_free; 292 } 293 break; 294 default: 295 ret = -EINVAL; 296 goto err_free; 297 } 298 299 if (clk_id == NULL) 300 return ctx; 301 302 ctx->clk = clk_get(dev, clk_id); 303 if (IS_ERR(ctx->clk)) { 304 ret = PTR_ERR(ctx->clk); 305 goto err_free; 306 } 307 308 ret = clk_prepare(ctx->clk); 309 if (ret < 0) { 310 clk_put(ctx->clk); 311 goto err_free; 312 } 313 314 return ctx; 315 316 err_free: 317 kfree(ctx); 318 319 return ERR_PTR(ret); 320 } 321 322 struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, 323 void __iomem *regs, 324 const struct regmap_config *config, 325 struct lock_class_key *lock_key, 326 const char *lock_name) 327 { 328 struct regmap_mmio_context *ctx; 329 330 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 331 if (IS_ERR(ctx)) 332 return ERR_CAST(ctx); 333 334 return __regmap_init(dev, ®map_mmio, ctx, config, 335 lock_key, lock_name); 336 } 337 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 338 339 struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, 340 const char *clk_id, 341 void __iomem *regs, 342 const struct regmap_config *config, 343 struct lock_class_key *lock_key, 344 const char *lock_name) 345 { 346 struct regmap_mmio_context *ctx; 347 348 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 349 if (IS_ERR(ctx)) 350 return ERR_CAST(ctx); 351 352 return __devm_regmap_init(dev, ®map_mmio, ctx, config, 353 lock_key, lock_name); 354 } 355 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 356 357 int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk) 358 { 359 struct regmap_mmio_context *ctx = map->bus_context; 360 361 ctx->clk = clk; 362 ctx->attached_clk = true; 363 364 return clk_prepare(ctx->clk); 365 } 366 EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk); 367 368 void regmap_mmio_detach_clk(struct regmap *map) 369 { 370 struct regmap_mmio_context *ctx = map->bus_context; 371 372 clk_unprepare(ctx->clk); 373 374 ctx->attached_clk = false; 375 ctx->clk = NULL; 376 } 377 EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk); 378 379 MODULE_LICENSE("GPL v2"); 380