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 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
  29static 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
  44static 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
  71static 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
  78static 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
  85static 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
  92static 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
  99static 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
 107static 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
 115static 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
 134static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
 135                                      unsigned int reg)
 136{
 137        return readb(ctx->regs + reg);
 138}
 139
 140static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
 141                                         unsigned int reg)
 142{
 143        return readw(ctx->regs + reg);
 144}
 145
 146static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
 147                                         unsigned int reg)
 148{
 149        return ioread16be(ctx->regs + reg);
 150}
 151
 152static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
 153                                         unsigned int reg)
 154{
 155        return readl(ctx->regs + reg);
 156}
 157
 158static 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
 165static 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
 172static 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
 191static 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
 203static 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
 211static 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, &regmap_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
 316err_free:
 317        kfree(ctx);
 318
 319        return ERR_PTR(ret);
 320}
 321
 322struct 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, &regmap_mmio, ctx, config,
 335                             lock_key, lock_name);
 336}
 337EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
 338
 339struct 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, &regmap_mmio, ctx, config,
 353                                  lock_key, lock_name);
 354}
 355EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
 356
 357int 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}
 366EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
 367
 368void 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}
 377EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
 378
 379MODULE_LICENSE("GPL v2");
 380