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 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 30 static 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 45 static 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 72 static 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 79 static 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 86 static 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 93 static 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 100 static 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 107 static 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 114 static 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 121 static 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 129 static 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 136 static 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 144 static 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 163 static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, 164 unsigned int reg) 165 { 166 return readb(ctx->regs + reg); 167 } 168 169 static 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 175 static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, 176 unsigned int reg) 177 { 178 return readw(ctx->regs + reg); 179 } 180 181 static 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 187 static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, 188 unsigned int reg) 189 { 190 return ioread16be(ctx->regs + reg); 191 } 192 193 static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, 194 unsigned int reg) 195 { 196 return readl(ctx->regs + reg); 197 } 198 199 static 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 205 static 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 212 static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx, 213 unsigned int reg) 214 { 215 return readq(ctx->regs + reg); 216 } 217 218 static 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 225 static 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 244 static 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 256 static 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 264 static 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 390 err_free: 391 kfree(ctx); 392 393 return ERR_PTR(ret); 394 } 395 396 struct 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 } 411 EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 412 413 struct 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 } 429 EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); 430 431 int 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 } 440 EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk); 441 442 void 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 } 451 EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk); 452 453 MODULE_LICENSE("GPL v2"); 454