linux/drivers/base/regmap/regmap-mmio.c
<<
>>
Prefs
   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, &regmap_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, &regmap_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, &regmap_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");
 454