Branch data Line data Source code
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 : 0 : 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 : 0 : static void regmap_mmio_write8(struct regmap_mmio_context *ctx, 72 : : unsigned int reg, 73 : : unsigned int val) 74 : : { 75 : 0 : writeb(val, ctx->regs + reg); 76 : 0 : } 77 : : 78 : 0 : static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, 79 : : unsigned int reg, 80 : : unsigned int val) 81 : : { 82 : 0 : writew(val, ctx->regs + reg); 83 : 0 : } 84 : : 85 : 0 : static void regmap_mmio_write16be(struct regmap_mmio_context *ctx, 86 : : unsigned int reg, 87 : : unsigned int val) 88 : : { 89 : 0 : iowrite16be(val, ctx->regs + reg); 90 : 0 : } 91 : : 92 : 0 : static void regmap_mmio_write32le(struct regmap_mmio_context *ctx, 93 : : unsigned int reg, 94 : : unsigned int val) 95 : : { 96 : 0 : writel(val, ctx->regs + reg); 97 : 0 : } 98 : : 99 : 0 : static void regmap_mmio_write32be(struct regmap_mmio_context *ctx, 100 : : unsigned int reg, 101 : : unsigned int val) 102 : : { 103 : 0 : iowrite32be(val, ctx->regs + reg); 104 : 0 : } 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 : 0 : 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 : 0 : if (!IS_ERR(ctx->clk)) { 121 : 0 : ret = clk_enable(ctx->clk); 122 : 0 : if (ret < 0) 123 : : return ret; 124 : : } 125 : : 126 : 0 : ctx->reg_write(ctx, reg, val); 127 : : 128 : 0 : if (!IS_ERR(ctx->clk)) 129 : 0 : clk_disable(ctx->clk); 130 : : 131 : : return 0; 132 : : } 133 : : 134 : 0 : static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, 135 : : unsigned int reg) 136 : : { 137 : 0 : return readb(ctx->regs + reg); 138 : : } 139 : : 140 : 0 : static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, 141 : : unsigned int reg) 142 : : { 143 : 0 : return readw(ctx->regs + reg); 144 : : } 145 : : 146 : 0 : static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, 147 : : unsigned int reg) 148 : : { 149 : 0 : return ioread16be(ctx->regs + reg); 150 : : } 151 : : 152 : 0 : static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, 153 : : unsigned int reg) 154 : : { 155 : 0 : return readl(ctx->regs + reg); 156 : : } 157 : : 158 : 0 : static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx, 159 : : unsigned int reg) 160 : : { 161 : 0 : 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 : 0 : 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 : 0 : if (!IS_ERR(ctx->clk)) { 178 : 0 : ret = clk_enable(ctx->clk); 179 : 0 : if (ret < 0) 180 : : return ret; 181 : : } 182 : : 183 : 0 : *val = ctx->reg_read(ctx, reg); 184 : : 185 : 0 : if (!IS_ERR(ctx->clk)) 186 : 0 : clk_disable(ctx->clk); 187 : : 188 : : return 0; 189 : : } 190 : : 191 : 0 : static void regmap_mmio_free_context(void *context) 192 : : { 193 : : struct regmap_mmio_context *ctx = context; 194 : : 195 : 0 : if (!IS_ERR(ctx->clk)) { 196 : 0 : clk_unprepare(ctx->clk); 197 : 0 : if (!ctx->attached_clk) 198 : 0 : clk_put(ctx->clk); 199 : : } 200 : 0 : kfree(context); 201 : 0 : } 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 : 0 : 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 : 0 : ret = regmap_mmio_regbits_check(config->reg_bits); 221 : 0 : if (ret) 222 : 0 : return ERR_PTR(ret); 223 : : 224 : 0 : if (config->pad_bits) 225 : : return ERR_PTR(-EINVAL); 226 : : 227 : 0 : min_stride = regmap_mmio_get_min_stride(config->val_bits); 228 : 0 : if (min_stride < 0) 229 : 0 : return ERR_PTR(min_stride); 230 : : 231 : 0 : if (config->reg_stride < min_stride) 232 : : return ERR_PTR(-EINVAL); 233 : : 234 : 0 : ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 235 : 0 : if (!ctx) 236 : : return ERR_PTR(-ENOMEM); 237 : : 238 : 0 : ctx->regs = regs; 239 : 0 : ctx->val_bytes = config->val_bits / 8; 240 : 0 : ctx->clk = ERR_PTR(-ENODEV); 241 : : 242 : 0 : 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 : 0 : switch (config->val_bits) { 249 : : case 8: 250 : 0 : ctx->reg_read = regmap_mmio_read8; 251 : 0 : ctx->reg_write = regmap_mmio_write8; 252 : 0 : break; 253 : : case 16: 254 : 0 : ctx->reg_read = regmap_mmio_read16le; 255 : 0 : ctx->reg_write = regmap_mmio_write16le; 256 : 0 : break; 257 : : case 32: 258 : 0 : ctx->reg_read = regmap_mmio_read32le; 259 : 0 : ctx->reg_write = regmap_mmio_write32le; 260 : 0 : 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 : 0 : switch (config->val_bits) { 277 : : case 8: 278 : 0 : ctx->reg_read = regmap_mmio_read8; 279 : 0 : ctx->reg_write = regmap_mmio_write8; 280 : 0 : break; 281 : : case 16: 282 : 0 : ctx->reg_read = regmap_mmio_read16be; 283 : 0 : ctx->reg_write = regmap_mmio_write16be; 284 : 0 : break; 285 : : case 32: 286 : 0 : ctx->reg_read = regmap_mmio_read32be; 287 : 0 : ctx->reg_write = regmap_mmio_write32be; 288 : 0 : 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 : 0 : if (clk_id == NULL) 300 : : return ctx; 301 : : 302 : 0 : ctx->clk = clk_get(dev, clk_id); 303 : 0 : if (IS_ERR(ctx->clk)) { 304 : : ret = PTR_ERR(ctx->clk); 305 : 0 : goto err_free; 306 : : } 307 : : 308 : 0 : ret = clk_prepare(ctx->clk); 309 : 0 : if (ret < 0) { 310 : 0 : clk_put(ctx->clk); 311 : 0 : goto err_free; 312 : : } 313 : : 314 : : return ctx; 315 : : 316 : : err_free: 317 : 0 : kfree(ctx); 318 : : 319 : 0 : return ERR_PTR(ret); 320 : : } 321 : : 322 : 0 : 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 : 0 : ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 331 : 0 : if (IS_ERR(ctx)) 332 : : return ERR_CAST(ctx); 333 : : 334 : 0 : return __regmap_init(dev, ®map_mmio, ctx, config, 335 : : lock_key, lock_name); 336 : : } 337 : : EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); 338 : : 339 : 0 : 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 : 0 : ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); 349 : 0 : if (IS_ERR(ctx)) 350 : : return ERR_CAST(ctx); 351 : : 352 : 0 : 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 : 0 : int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk) 358 : : { 359 : 0 : struct regmap_mmio_context *ctx = map->bus_context; 360 : : 361 : 0 : ctx->clk = clk; 362 : 0 : ctx->attached_clk = true; 363 : : 364 : 0 : return clk_prepare(ctx->clk); 365 : : } 366 : : EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk); 367 : : 368 : 0 : void regmap_mmio_detach_clk(struct regmap *map) 369 : : { 370 : 0 : struct regmap_mmio_context *ctx = map->bus_context; 371 : : 372 : 0 : clk_unprepare(ctx->clk); 373 : : 374 : 0 : ctx->attached_clk = false; 375 : 0 : ctx->clk = NULL; 376 : 0 : } 377 : : EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk); 378 : : 379 : : MODULE_LICENSE("GPL v2");