linux/drivers/base/regmap/regmap-mmio.c
<<
>>
Prefs
   1/*
   2 * Register map access API - MMIO support
   3 *
   4 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include <linux/clk.h>
  20#include <linux/err.h>
  21#include <linux/io.h>
  22#include <linux/module.h>
  23#include <linux/regmap.h>
  24#include <linux/slab.h>
  25
  26#include "internal.h"
  27
  28struct regmap_mmio_context {
  29        void __iomem *regs;
  30        unsigned val_bytes;
  31
  32        bool attached_clk;
  33        struct clk *clk;
  34
  35        void (*reg_write)(struct regmap_mmio_context *ctx,
  36                          unsigned int reg, unsigned int val);
  37        unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
  38                                 unsigned int reg);
  39};
  40
  41static int regmap_mmio_regbits_check(size_t reg_bits)
  42{
  43        switch (reg_bits) {
  44        case 8:
  45        case 16:
  46        case 32:
  47#ifdef CONFIG_64BIT
  48        case 64:
  49#endif
  50                return 0;
  51        default:
  52                return -EINVAL;
  53        }
  54}
  55
  56static int regmap_mmio_get_min_stride(size_t val_bits)
  57{
  58        int min_stride;
  59
  60        switch (val_bits) {
  61        case 8:
  62                /* The core treats 0 as 1 */
  63                min_stride = 0;
  64                return 0;
  65        case 16:
  66                min_stride = 2;
  67                break;
  68        case 32:
  69                min_stride = 4;
  70                break;
  71#ifdef CONFIG_64BIT
  72        case 64:
  73                min_stride = 8;
  74                break;
  75#endif
  76        default:
  77                return -EINVAL;
  78        }
  79
  80        return min_stride;
  81}
  82
  83static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
  84                                unsigned int reg,
  85                                unsigned int val)
  86{
  87        writeb(val, ctx->regs + reg);
  88}
  89
  90static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
  91                                  unsigned int reg,
  92                                  unsigned int val)
  93{
  94        writew(val, ctx->regs + reg);
  95}
  96
  97static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
  98                                  unsigned int reg,
  99                                  unsigned int val)
 100{
 101        iowrite16be(val, ctx->regs + reg);
 102}
 103
 104static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
 105                                  unsigned int reg,
 106                                  unsigned int val)
 107{
 108        writel(val, ctx->regs + reg);
 109}
 110
 111static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
 112                                  unsigned int reg,
 113                                  unsigned int val)
 114{
 115        iowrite32be(val, ctx->regs + reg);
 116}
 117
 118#ifdef CONFIG_64BIT
 119static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
 120                                  unsigned int reg,
 121                                  unsigned int val)
 122{
 123        writeq(val, ctx->regs + reg);
 124}
 125#endif
 126
 127static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
 128{
 129        struct regmap_mmio_context *ctx = context;
 130        int ret;
 131
 132        if (!IS_ERR(ctx->clk)) {
 133                ret = clk_enable(ctx->clk);
 134                if (ret < 0)
 135                        return ret;
 136        }
 137
 138        ctx->reg_write(ctx, reg, val);
 139
 140        if (!IS_ERR(ctx->clk))
 141                clk_disable(ctx->clk);
 142
 143        return 0;
 144}
 145
 146static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
 147                                      unsigned int reg)
 148{
 149        return readb(ctx->regs + reg);
 150}
 151
 152static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
 153                                         unsigned int reg)
 154{
 155        return readw(ctx->regs + reg);
 156}
 157
 158static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
 159                                         unsigned int reg)
 160{
 161        return ioread16be(ctx->regs + reg);
 162}
 163
 164static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
 165                                         unsigned int reg)
 166{
 167        return readl(ctx->regs + reg);
 168}
 169
 170static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
 171                                         unsigned int reg)
 172{
 173        return ioread32be(ctx->regs + reg);
 174}
 175
 176#ifdef CONFIG_64BIT
 177static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
 178                                         unsigned int reg)
 179{
 180        return readq(ctx->regs + reg);
 181}
 182#endif
 183
 184static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
 185{
 186        struct regmap_mmio_context *ctx = context;
 187        int ret;
 188
 189        if (!IS_ERR(ctx->clk)) {
 190                ret = clk_enable(ctx->clk);
 191                if (ret < 0)
 192                        return ret;
 193        }
 194
 195        *val = ctx->reg_read(ctx, reg);
 196
 197        if (!IS_ERR(ctx->clk))
 198                clk_disable(ctx->clk);
 199
 200        return 0;
 201}
 202
 203static void regmap_mmio_free_context(void *context)
 204{
 205        struct regmap_mmio_context *ctx = context;
 206
 207        if (!IS_ERR(ctx->clk)) {
 208                clk_unprepare(ctx->clk);
 209                if (!ctx->attached_clk)
 210                        clk_put(ctx->clk);
 211        }
 212        kfree(context);
 213}
 214
 215static const struct regmap_bus regmap_mmio = {
 216        .fast_io = true,
 217        .reg_write = regmap_mmio_write,
 218        .reg_read = regmap_mmio_read,
 219        .free_context = regmap_mmio_free_context,
 220        .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
 221};
 222
 223static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
 224                                        const char *clk_id,
 225                                        void __iomem *regs,
 226                                        const struct regmap_config *config)
 227{
 228        struct regmap_mmio_context *ctx;
 229        int min_stride;
 230        int ret;
 231
 232        ret = regmap_mmio_regbits_check(config->reg_bits);
 233        if (ret)
 234                return ERR_PTR(ret);
 235
 236        if (config->pad_bits)
 237                return ERR_PTR(-EINVAL);
 238
 239        min_stride = regmap_mmio_get_min_stride(config->val_bits);
 240        if (min_stride < 0)
 241                return ERR_PTR(min_stride);
 242
 243        if (config->reg_stride < min_stride)
 244                return ERR_PTR(-EINVAL);
 245
 246        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 247        if (!ctx)
 248                return ERR_PTR(-ENOMEM);
 249
 250        ctx->regs = regs;
 251        ctx->val_bytes = config->val_bits / 8;
 252        ctx->clk = ERR_PTR(-ENODEV);
 253
 254        switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
 255        case REGMAP_ENDIAN_DEFAULT:
 256        case REGMAP_ENDIAN_LITTLE:
 257#ifdef __LITTLE_ENDIAN
 258        case REGMAP_ENDIAN_NATIVE:
 259#endif
 260                switch (config->val_bits) {
 261                case 8:
 262                        ctx->reg_read = regmap_mmio_read8;
 263                        ctx->reg_write = regmap_mmio_write8;
 264                        break;
 265                case 16:
 266                        ctx->reg_read = regmap_mmio_read16le;
 267                        ctx->reg_write = regmap_mmio_write16le;
 268                        break;
 269                case 32:
 270                        ctx->reg_read = regmap_mmio_read32le;
 271                        ctx->reg_write = regmap_mmio_write32le;
 272                        break;
 273#ifdef CONFIG_64BIT
 274                case 64:
 275                        ctx->reg_read = regmap_mmio_read64le;
 276                        ctx->reg_write = regmap_mmio_write64le;
 277                        break;
 278#endif
 279                default:
 280                        ret = -EINVAL;
 281                        goto err_free;
 282                }
 283                break;
 284        case REGMAP_ENDIAN_BIG:
 285#ifdef __BIG_ENDIAN
 286        case REGMAP_ENDIAN_NATIVE:
 287#endif
 288                switch (config->val_bits) {
 289                case 8:
 290                        ctx->reg_read = regmap_mmio_read8;
 291                        ctx->reg_write = regmap_mmio_write8;
 292                        break;
 293                case 16:
 294                        ctx->reg_read = regmap_mmio_read16be;
 295                        ctx->reg_write = regmap_mmio_write16be;
 296                        break;
 297                case 32:
 298                        ctx->reg_read = regmap_mmio_read32be;
 299                        ctx->reg_write = regmap_mmio_write32be;
 300                        break;
 301                default:
 302                        ret = -EINVAL;
 303                        goto err_free;
 304                }
 305                break;
 306        default:
 307                ret = -EINVAL;
 308                goto err_free;
 309        }
 310
 311        if (clk_id == NULL)
 312                return ctx;
 313
 314        ctx->clk = clk_get(dev, clk_id);
 315        if (IS_ERR(ctx->clk)) {
 316                ret = PTR_ERR(ctx->clk);
 317                goto err_free;
 318        }
 319
 320        ret = clk_prepare(ctx->clk);
 321        if (ret < 0) {
 322                clk_put(ctx->clk);
 323                goto err_free;
 324        }
 325
 326        return ctx;
 327
 328err_free:
 329        kfree(ctx);
 330
 331        return ERR_PTR(ret);
 332}
 333
 334struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 335                                      void __iomem *regs,
 336                                      const struct regmap_config *config,
 337                                      struct lock_class_key *lock_key,
 338                                      const char *lock_name)
 339{
 340        struct regmap_mmio_context *ctx;
 341
 342        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 343        if (IS_ERR(ctx))
 344                return ERR_CAST(ctx);
 345
 346        return __regmap_init(dev, &regmap_mmio, ctx, config,
 347                             lock_key, lock_name);
 348}
 349EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
 350
 351struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
 352                                           const char *clk_id,
 353                                           void __iomem *regs,
 354                                           const struct regmap_config *config,
 355                                           struct lock_class_key *lock_key,
 356                                           const char *lock_name)
 357{
 358        struct regmap_mmio_context *ctx;
 359
 360        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 361        if (IS_ERR(ctx))
 362                return ERR_CAST(ctx);
 363
 364        return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
 365                                  lock_key, lock_name);
 366}
 367EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
 368
 369int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
 370{
 371        struct regmap_mmio_context *ctx = map->bus_context;
 372
 373        ctx->clk = clk;
 374        ctx->attached_clk = true;
 375
 376        return clk_prepare(ctx->clk);
 377}
 378EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk);
 379
 380void regmap_mmio_detach_clk(struct regmap *map)
 381{
 382        struct regmap_mmio_context *ctx = map->bus_context;
 383
 384        clk_unprepare(ctx->clk);
 385
 386        ctx->attached_clk = false;
 387        ctx->clk = NULL;
 388}
 389EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
 390
 391MODULE_LICENSE("GPL v2");
 392