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        struct clk *clk;
  32
  33        void (*reg_write)(struct regmap_mmio_context *ctx,
  34                          unsigned int reg, unsigned int val);
  35        unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
  36                                 unsigned int reg);
  37};
  38
  39static int regmap_mmio_regbits_check(size_t reg_bits)
  40{
  41        switch (reg_bits) {
  42        case 8:
  43        case 16:
  44        case 32:
  45#ifdef CONFIG_64BIT
  46        case 64:
  47#endif
  48                return 0;
  49        default:
  50                return -EINVAL;
  51        }
  52}
  53
  54static int regmap_mmio_get_min_stride(size_t val_bits)
  55{
  56        int min_stride;
  57
  58        switch (val_bits) {
  59        case 8:
  60                /* The core treats 0 as 1 */
  61                min_stride = 0;
  62                return 0;
  63        case 16:
  64                min_stride = 2;
  65                break;
  66        case 32:
  67                min_stride = 4;
  68                break;
  69#ifdef CONFIG_64BIT
  70        case 64:
  71                min_stride = 8;
  72                break;
  73#endif
  74        default:
  75                return -EINVAL;
  76        }
  77
  78        return min_stride;
  79}
  80
  81static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
  82                                unsigned int reg,
  83                                unsigned int val)
  84{
  85        writeb(val, ctx->regs + reg);
  86}
  87
  88static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
  89                                  unsigned int reg,
  90                                  unsigned int val)
  91{
  92        writew(val, ctx->regs + reg);
  93}
  94
  95static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
  96                                  unsigned int reg,
  97                                  unsigned int val)
  98{
  99        iowrite16be(val, ctx->regs + reg);
 100}
 101
 102static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
 103                                  unsigned int reg,
 104                                  unsigned int val)
 105{
 106        writel(val, ctx->regs + reg);
 107}
 108
 109static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
 110                                  unsigned int reg,
 111                                  unsigned int val)
 112{
 113        iowrite32be(val, ctx->regs + reg);
 114}
 115
 116#ifdef CONFIG_64BIT
 117static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
 118                                  unsigned int reg,
 119                                  unsigned int val)
 120{
 121        writeq(val, ctx->regs + reg);
 122}
 123#endif
 124
 125static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
 126{
 127        struct regmap_mmio_context *ctx = context;
 128        int ret;
 129
 130        if (!IS_ERR(ctx->clk)) {
 131                ret = clk_enable(ctx->clk);
 132                if (ret < 0)
 133                        return ret;
 134        }
 135
 136        ctx->reg_write(ctx, reg, val);
 137
 138        if (!IS_ERR(ctx->clk))
 139                clk_disable(ctx->clk);
 140
 141        return 0;
 142}
 143
 144static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
 145                                      unsigned int reg)
 146{
 147        return readb(ctx->regs + reg);
 148}
 149
 150static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
 151                                         unsigned int reg)
 152{
 153        return readw(ctx->regs + reg);
 154}
 155
 156static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
 157                                         unsigned int reg)
 158{
 159        return ioread16be(ctx->regs + reg);
 160}
 161
 162static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
 163                                         unsigned int reg)
 164{
 165        return readl(ctx->regs + reg);
 166}
 167
 168static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
 169                                         unsigned int reg)
 170{
 171        return ioread32be(ctx->regs + reg);
 172}
 173
 174#ifdef CONFIG_64BIT
 175static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
 176                                         unsigned int reg)
 177{
 178        return readq(ctx->regs + reg);
 179}
 180#endif
 181
 182static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
 183{
 184        struct regmap_mmio_context *ctx = context;
 185        int ret;
 186
 187        if (!IS_ERR(ctx->clk)) {
 188                ret = clk_enable(ctx->clk);
 189                if (ret < 0)
 190                        return ret;
 191        }
 192
 193        *val = ctx->reg_read(ctx, reg);
 194
 195        if (!IS_ERR(ctx->clk))
 196                clk_disable(ctx->clk);
 197
 198        return 0;
 199}
 200
 201static void regmap_mmio_free_context(void *context)
 202{
 203        struct regmap_mmio_context *ctx = context;
 204
 205        if (!IS_ERR(ctx->clk)) {
 206                clk_unprepare(ctx->clk);
 207                clk_put(ctx->clk);
 208        }
 209        kfree(context);
 210}
 211
 212static const struct regmap_bus regmap_mmio = {
 213        .fast_io = true,
 214        .reg_write = regmap_mmio_write,
 215        .reg_read = regmap_mmio_read,
 216        .free_context = regmap_mmio_free_context,
 217        .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
 218};
 219
 220static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
 221                                        const char *clk_id,
 222                                        void __iomem *regs,
 223                                        const struct regmap_config *config)
 224{
 225        struct regmap_mmio_context *ctx;
 226        int min_stride;
 227        int ret;
 228
 229        ret = regmap_mmio_regbits_check(config->reg_bits);
 230        if (ret)
 231                return ERR_PTR(ret);
 232
 233        if (config->pad_bits)
 234                return ERR_PTR(-EINVAL);
 235
 236        min_stride = regmap_mmio_get_min_stride(config->val_bits);
 237        if (min_stride < 0)
 238                return ERR_PTR(min_stride);
 239
 240        if (config->reg_stride < min_stride)
 241                return ERR_PTR(-EINVAL);
 242
 243        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 244        if (!ctx)
 245                return ERR_PTR(-ENOMEM);
 246
 247        ctx->regs = regs;
 248        ctx->val_bytes = config->val_bits / 8;
 249        ctx->clk = ERR_PTR(-ENODEV);
 250
 251        switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
 252        case REGMAP_ENDIAN_DEFAULT:
 253        case REGMAP_ENDIAN_LITTLE:
 254#ifdef __LITTLE_ENDIAN
 255        case REGMAP_ENDIAN_NATIVE:
 256#endif
 257                switch (config->val_bits) {
 258                case 8:
 259                        ctx->reg_read = regmap_mmio_read8;
 260                        ctx->reg_write = regmap_mmio_write8;
 261                        break;
 262                case 16:
 263                        ctx->reg_read = regmap_mmio_read16le;
 264                        ctx->reg_write = regmap_mmio_write16le;
 265                        break;
 266                case 32:
 267                        ctx->reg_read = regmap_mmio_read32le;
 268                        ctx->reg_write = regmap_mmio_write32le;
 269                        break;
 270#ifdef CONFIG_64BIT
 271                case 64:
 272                        ctx->reg_read = regmap_mmio_read64le;
 273                        ctx->reg_write = regmap_mmio_write64le;
 274                        break;
 275#endif
 276                default:
 277                        ret = -EINVAL;
 278                        goto err_free;
 279                }
 280                break;
 281        case REGMAP_ENDIAN_BIG:
 282#ifdef __BIG_ENDIAN
 283        case REGMAP_ENDIAN_NATIVE:
 284#endif
 285                switch (config->val_bits) {
 286                case 8:
 287                        ctx->reg_read = regmap_mmio_read8;
 288                        ctx->reg_write = regmap_mmio_write8;
 289                        break;
 290                case 16:
 291                        ctx->reg_read = regmap_mmio_read16be;
 292                        ctx->reg_write = regmap_mmio_write16be;
 293                        break;
 294                case 32:
 295                        ctx->reg_read = regmap_mmio_read32be;
 296                        ctx->reg_write = regmap_mmio_write32be;
 297                        break;
 298                default:
 299                        ret = -EINVAL;
 300                        goto err_free;
 301                }
 302                break;
 303        default:
 304                ret = -EINVAL;
 305                goto err_free;
 306        }
 307
 308        if (clk_id == NULL)
 309                return ctx;
 310
 311        ctx->clk = clk_get(dev, clk_id);
 312        if (IS_ERR(ctx->clk)) {
 313                ret = PTR_ERR(ctx->clk);
 314                goto err_free;
 315        }
 316
 317        ret = clk_prepare(ctx->clk);
 318        if (ret < 0) {
 319                clk_put(ctx->clk);
 320                goto err_free;
 321        }
 322
 323        return ctx;
 324
 325err_free:
 326        kfree(ctx);
 327
 328        return ERR_PTR(ret);
 329}
 330
 331struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 332                                      void __iomem *regs,
 333                                      const struct regmap_config *config,
 334                                      struct lock_class_key *lock_key,
 335                                      const char *lock_name)
 336{
 337        struct regmap_mmio_context *ctx;
 338
 339        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 340        if (IS_ERR(ctx))
 341                return ERR_CAST(ctx);
 342
 343        return __regmap_init(dev, &regmap_mmio, ctx, config,
 344                             lock_key, lock_name);
 345}
 346EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk);
 347
 348struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
 349                                           const char *clk_id,
 350                                           void __iomem *regs,
 351                                           const struct regmap_config *config,
 352                                           struct lock_class_key *lock_key,
 353                                           const char *lock_name)
 354{
 355        struct regmap_mmio_context *ctx;
 356
 357        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 358        if (IS_ERR(ctx))
 359                return ERR_CAST(ctx);
 360
 361        return __devm_regmap_init(dev, &regmap_mmio, ctx, config,
 362                                  lock_key, lock_name);
 363}
 364EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk);
 365
 366MODULE_LICENSE("GPL v2");
 367