regmap-mmio.c (9762B)
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 16struct regmap_mmio_context { 17 void __iomem *regs; 18 unsigned int val_bytes; 19 bool relaxed_mmio; 20 21 bool attached_clk; 22 struct clk *clk; 23 24 void (*reg_write)(struct regmap_mmio_context *ctx, 25 unsigned int reg, unsigned int val); 26 unsigned int (*reg_read)(struct regmap_mmio_context *ctx, 27 unsigned int reg); 28}; 29 30static int regmap_mmio_regbits_check(size_t reg_bits) 31{ 32 switch (reg_bits) { 33 case 8: 34 case 16: 35 case 32: 36#ifdef CONFIG_64BIT 37 case 64: 38#endif 39 return 0; 40 default: 41 return -EINVAL; 42 } 43} 44 45static int regmap_mmio_get_min_stride(size_t val_bits) 46{ 47 int min_stride; 48 49 switch (val_bits) { 50 case 8: 51 /* The core treats 0 as 1 */ 52 min_stride = 0; 53 return 0; 54 case 16: 55 min_stride = 2; 56 break; 57 case 32: 58 min_stride = 4; 59 break; 60#ifdef CONFIG_64BIT 61 case 64: 62 min_stride = 8; 63 break; 64#endif 65 default: 66 return -EINVAL; 67 } 68 69 return min_stride; 70} 71 72static void regmap_mmio_write8(struct regmap_mmio_context *ctx, 73 unsigned int reg, 74 unsigned int val) 75{ 76 writeb(val, ctx->regs + reg); 77} 78 79static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx, 80 unsigned int reg, 81 unsigned int val) 82{ 83 writeb_relaxed(val, ctx->regs + reg); 84} 85 86static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, 87 unsigned int reg, 88 unsigned int val) 89{ 90 writew(val, ctx->regs + reg); 91} 92 93static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx, 94 unsigned int reg, 95 unsigned int val) 96{ 97 writew_relaxed(val, ctx->regs + reg); 98} 99 100static void regmap_mmio_write16be(struct regmap_mmio_context *ctx, 101 unsigned int reg, 102 unsigned int val) 103{ 104 iowrite16be(val, ctx->regs + reg); 105} 106 107static void regmap_mmio_write32le(struct regmap_mmio_context *ctx, 108 unsigned int reg, 109 unsigned int val) 110{ 111 writel(val, ctx->regs + reg); 112} 113 114static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx, 115 unsigned int reg, 116 unsigned int val) 117{ 118 writel_relaxed(val, ctx->regs + reg); 119} 120 121static void regmap_mmio_write32be(struct regmap_mmio_context *ctx, 122 unsigned int reg, 123 unsigned int val) 124{ 125 iowrite32be(val, ctx->regs + reg); 126} 127 128#ifdef CONFIG_64BIT 129static void regmap_mmio_write64le(struct regmap_mmio_context *ctx, 130 unsigned int reg, 131 unsigned int val) 132{ 133 writeq(val, ctx->regs + reg); 134} 135 136static void regmap_mmio_write64le_relaxed(struct regmap_mmio_context *ctx, 137 unsigned int reg, 138 unsigned int val) 139{ 140 writeq_relaxed(val, ctx->regs + reg); 141} 142#endif 143 144static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val) 145{ 146 struct regmap_mmio_context *ctx = context; 147 int ret; 148 149 if (!IS_ERR(ctx->clk)) { 150 ret = clk_enable(ctx->clk); 151 if (ret < 0) 152 return ret; 153 } 154 155 ctx->reg_write(ctx, reg, val); 156 157 if (!IS_ERR(ctx->clk)) 158 clk_disable(ctx->clk); 159 160 return 0; 161} 162 163static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, 164 unsigned int reg) 165{ 166 return readb(ctx->regs + reg); 167} 168 169static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx, 170 unsigned int reg) 171{ 172 return readb_relaxed(ctx->regs + reg); 173} 174 175static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, 176 unsigned int reg) 177{ 178 return readw(ctx->regs + reg); 179} 180 181static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx, 182 unsigned int reg) 183{ 184 return readw_relaxed(ctx->regs + reg); 185} 186 187static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, 188 unsigned int reg) 189{ 190 return ioread16be(ctx->regs + reg); 191} 192 193static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, 194 unsigned int reg) 195{ 196 return readl(ctx->regs + reg); 197} 198 199static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx, 200 unsigned int reg) 201{ 202 return readl_relaxed(ctx->regs + reg); 203} 204 205static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx, 206 unsigned int reg) 207{ 208 return ioread32be(ctx->regs + reg); 209} 210 211#ifdef CONFIG_64BIT 212static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx, 213 unsigned int reg) 214{ 215 return readq(ctx->regs + reg); 216} 217 218static unsigned int regmap_mmio_read64le_relaxed(struct regmap_mmio_context *ctx, 219 unsigned int reg) 220{ 221 return readq_relaxed(ctx->regs + reg); 222} 223#endif 224 225static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val) 226{ 227 struct regmap_mmio_context *ctx = context; 228 int ret; 229 230 if (!IS_ERR(ctx->clk)) { 231 ret = clk_enable(ctx->clk); 232 if (ret < 0) 233 return ret; 234 } 235 236 *val = ctx->reg_read(ctx, reg); 237 238 if (!IS_ERR(ctx->clk)) 239 clk_disable(ctx->clk); 240 241 return 0; 242} 243 244static void regmap_mmio_free_context(void *context) 245{ 246 struct regmap_mmio_context *ctx = context; 247 248 if (!IS_ERR(ctx->clk)) { 249 clk_unprepare(ctx->clk); 250 if (!ctx->attached_clk) 251 clk_put(ctx->clk); 252 } 253 kfree(context); 254} 255 256static const struct regmap_bus regmap_mmio = { 257 .fast_io = true, 258 .reg_write = regmap_mmio_write, 259 .reg_read = regmap_mmio_read, 260 .free_context = regmap_mmio_free_context, 261 .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 262}; 263 264static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, 265 const char *clk_id, 266 void __iomem *regs, 267 const struct regmap_config *config) 268{ 269 struct regmap_mmio_context *ctx; 270 int min_stride; 271 int ret; 272 273 ret = regmap_mmio_regbits_check(config->reg_bits); 274 if (ret) 275 return ERR_PTR(ret); 276 277 if (config->pad_bits) 278 return ERR_PTR(-EINVAL); 279 280 min_stride = regmap_mmio_get_min_stride(config->val_bits); 281 if (min_stride < 0) 282 return ERR_PTR(min_stride); 283 284 if (config->reg_stride < min_stride) 285 return ERR_PTR(-EINVAL); 286 287 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 288 if (!ctx) 289 return ERR_PTR(-ENOMEM); 290 291 ctx->regs = regs; 292 ctx->val_bytes = config->val_bits / 8; 293 ctx->relaxed_mmio = config->use_relaxed_mmio; 294 ctx->clk = ERR_PTR(-ENODEV); 295 296 switch (regmap_get_val_endian(dev, ®map_mmio, config)) { 297 case REGMAP_ENDIAN_DEFAULT: 298 case REGMAP_ENDIAN_LITTLE: 299#ifdef __LITTLE_ENDIAN 300 case REGMAP_ENDIAN_NATIVE: 301#endif 302 switch (config->val_bits) { 303 case 8: 304 if (ctx->relaxed_mmio) { 305 ctx->reg_read = regmap_mmio_read8_relaxed; 306 ctx->reg_write = regmap_mmio_write8_relaxed; 307 } else { 308 ctx->reg_read = regmap_mmio_read8; 309 ctx->reg_write = regmap_mmio_write8; 310 } 311 break; 312 case 16: 313 if (ctx->relaxed_mmio) { 314 ctx->reg_read = regmap_mmio_read16le_relaxed; 315 ctx->reg_write = regmap_mmio_write16le_relaxed; 316 } else { 317 ctx->reg_read = regmap_mmio_read16le; 318 ctx->reg_write = regmap_mmio_write16le; 319 } 320 break; 321 case 32: 322 if (ctx->relaxed_mmio) { 323 ctx->reg_read = regmap_mmio_read32le_relaxed; 324 ctx->reg_write = regmap_mmio_write32le_relaxed; 325 } else { 326 ctx->reg_read = regmap_mmio_read32le; 327 ctx->reg_write = regmap_mmio_write32le; 328 } 329 break; 330#ifdef CONFIG_64BIT 331 case 64: 332 if (ctx->relaxed_mmio) { 333 ctx->reg_read = regmap_mmio_read64le_relaxed; 334 ctx->reg_write = regmap_mmio_write64le_relaxed; 335 } else { 336 ctx->reg_read = regmap_mmio_read64le; 337 ctx->reg_write = regmap_mmio_write64le; 338 } 339 break; 340#endif 341 default: 342 ret = -EINVAL; 343 goto err_free; 344 } 345 break; 346 case REGMAP_ENDIAN_BIG: 347#ifdef __BIG_ENDIAN 348 case REGMAP_ENDIAN_NATIVE: 349#endif 350 switch (config->val_bits) { 351 case 8: 352 ctx->reg_read = regmap_mmio_read8; 353 ctx->reg_write = regmap_mmio_write8; 354 break; 355 case 16: 356 ctx->reg_read = regmap_mmio_read16be; 357 ctx->reg_write = regmap_mmio_write16be; 358 break; 359 case 32: 360 ctx->reg_read = regmap_mmio_read32be; 361 ctx->reg_write = regmap_mmio_write32be; 362 break; 363 default: 364 ret = -EINVAL; 365 goto err_free; 366 } 367 break; 368 default: 369 ret = -EINVAL; 370 goto err_free; 371 } 372 373 if (clk_id == NULL) 374 return ctx; 375 376 ctx->clk = clk_get(dev, clk_id); 377 if (IS_ERR(ctx->clk)) { 378 ret = PTR_ERR(ctx->clk); 379 goto err_free; 380 } 381 382 ret = clk_prepare(ctx->clk); 383 if (ret < 0) { 384 clk_put(ctx->clk); 385 goto err_free; 386 } 387 388 return ctx; 389 390err_free: 391 kfree(ctx); 392 393 return ERR_PTR(ret); 394} 395 396struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, 397 void __iomem *regs, 398 const struct regmap_config *config, 399 struct lock_class_key *lock_key, 400 const char *lock_name) 401{ 402 struct regmap_mmio_context *ctx; 403 404 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 405 if (IS_ERR(ctx)) 406 return ERR_CAST(ctx); 407 408 return __regmap_init(dev, ®map_mmio, ctx, config, 409 lock_key, lock_name); 410} 411EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 412 413struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, 414 const char *clk_id, 415 void __iomem *regs, 416 const struct regmap_config *config, 417 struct lock_class_key *lock_key, 418 const char *lock_name) 419{ 420 struct regmap_mmio_context *ctx; 421 422 ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 423 if (IS_ERR(ctx)) 424 return ERR_CAST(ctx); 425 426 return __devm_regmap_init(dev, ®map_mmio, ctx, config, 427 lock_key, lock_name); 428} 429EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 430 431int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk) 432{ 433 struct regmap_mmio_context *ctx = map->bus_context; 434 435 ctx->clk = clk; 436 ctx->attached_clk = true; 437 438 return clk_prepare(ctx->clk); 439} 440EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk); 441 442void regmap_mmio_detach_clk(struct regmap *map) 443{ 444 struct regmap_mmio_context *ctx = map->bus_context; 445 446 clk_unprepare(ctx->clk); 447 448 ctx->attached_clk = false; 449 ctx->clk = NULL; 450} 451EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk); 452 453MODULE_LICENSE("GPL v2");