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/init.h>
  22#include <linux/io.h>
  23#include <linux/module.h>
  24#include <linux/regmap.h>
  25#include <linux/slab.h>
  26
  27struct regmap_mmio_context {
  28        void __iomem *regs;
  29        unsigned val_bytes;
  30        struct clk *clk;
  31};
  32
  33static int regmap_mmio_gather_write(void *context,
  34                                    const void *reg, size_t reg_size,
  35                                    const void *val, size_t val_size)
  36{
  37        struct regmap_mmio_context *ctx = context;
  38        u32 offset;
  39        int ret;
  40
  41        BUG_ON(reg_size != 4);
  42
  43        if (ctx->clk) {
  44                ret = clk_enable(ctx->clk);
  45                if (ret < 0)
  46                        return ret;
  47        }
  48
  49        offset = *(u32 *)reg;
  50
  51        while (val_size) {
  52                switch (ctx->val_bytes) {
  53                case 1:
  54                        writeb(*(u8 *)val, ctx->regs + offset);
  55                        break;
  56                case 2:
  57                        writew(*(u16 *)val, ctx->regs + offset);
  58                        break;
  59                case 4:
  60                        writel(*(u32 *)val, ctx->regs + offset);
  61                        break;
  62#ifdef CONFIG_64BIT
  63                case 8:
  64                        writeq(*(u64 *)val, ctx->regs + offset);
  65                        break;
  66#endif
  67                default:
  68                        /* Should be caught by regmap_mmio_check_config */
  69                        BUG();
  70                }
  71                val_size -= ctx->val_bytes;
  72                val += ctx->val_bytes;
  73                offset += ctx->val_bytes;
  74        }
  75
  76        if (ctx->clk)
  77                clk_disable(ctx->clk);
  78
  79        return 0;
  80}
  81
  82static int regmap_mmio_write(void *context, const void *data, size_t count)
  83{
  84        BUG_ON(count < 4);
  85
  86        return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
  87}
  88
  89static int regmap_mmio_read(void *context,
  90                            const void *reg, size_t reg_size,
  91                            void *val, size_t val_size)
  92{
  93        struct regmap_mmio_context *ctx = context;
  94        u32 offset;
  95        int ret;
  96
  97        BUG_ON(reg_size != 4);
  98
  99        if (ctx->clk) {
 100                ret = clk_enable(ctx->clk);
 101                if (ret < 0)
 102                        return ret;
 103        }
 104
 105        offset = *(u32 *)reg;
 106
 107        while (val_size) {
 108                switch (ctx->val_bytes) {
 109                case 1:
 110                        *(u8 *)val = readb(ctx->regs + offset);
 111                        break;
 112                case 2:
 113                        *(u16 *)val = readw(ctx->regs + offset);
 114                        break;
 115                case 4:
 116                        *(u32 *)val = readl(ctx->regs + offset);
 117                        break;
 118#ifdef CONFIG_64BIT
 119                case 8:
 120                        *(u64 *)val = readq(ctx->regs + offset);
 121                        break;
 122#endif
 123                default:
 124                        /* Should be caught by regmap_mmio_check_config */
 125                        BUG();
 126                }
 127                val_size -= ctx->val_bytes;
 128                val += ctx->val_bytes;
 129                offset += ctx->val_bytes;
 130        }
 131
 132        if (ctx->clk)
 133                clk_disable(ctx->clk);
 134
 135        return 0;
 136}
 137
 138static void regmap_mmio_free_context(void *context)
 139{
 140        struct regmap_mmio_context *ctx = context;
 141
 142        if (ctx->clk) {
 143                clk_unprepare(ctx->clk);
 144                clk_put(ctx->clk);
 145        }
 146        kfree(context);
 147}
 148
 149static struct regmap_bus regmap_mmio = {
 150        .fast_io = true,
 151        .write = regmap_mmio_write,
 152        .gather_write = regmap_mmio_gather_write,
 153        .read = regmap_mmio_read,
 154        .free_context = regmap_mmio_free_context,
 155        .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
 156        .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
 157};
 158
 159static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
 160                                        const char *clk_id,
 161                                        void __iomem *regs,
 162                                        const struct regmap_config *config)
 163{
 164        struct regmap_mmio_context *ctx;
 165        int min_stride;
 166        int ret;
 167
 168        if (config->reg_bits != 32)
 169                return ERR_PTR(-EINVAL);
 170
 171        if (config->pad_bits)
 172                return ERR_PTR(-EINVAL);
 173
 174        switch (config->val_bits) {
 175        case 8:
 176                /* The core treats 0 as 1 */
 177                min_stride = 0;
 178                break;
 179        case 16:
 180                min_stride = 2;
 181                break;
 182        case 32:
 183                min_stride = 4;
 184                break;
 185#ifdef CONFIG_64BIT
 186        case 64:
 187                min_stride = 8;
 188                break;
 189#endif
 190                break;
 191        default:
 192                return ERR_PTR(-EINVAL);
 193        }
 194
 195        if (config->reg_stride < min_stride)
 196                return ERR_PTR(-EINVAL);
 197
 198        switch (config->reg_format_endian) {
 199        case REGMAP_ENDIAN_DEFAULT:
 200        case REGMAP_ENDIAN_NATIVE:
 201                break;
 202        default:
 203                return ERR_PTR(-EINVAL);
 204        }
 205
 206        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 207        if (!ctx)
 208                return ERR_PTR(-ENOMEM);
 209
 210        ctx->regs = regs;
 211        ctx->val_bytes = config->val_bits / 8;
 212
 213        if (clk_id == NULL)
 214                return ctx;
 215
 216        ctx->clk = clk_get(dev, clk_id);
 217        if (IS_ERR(ctx->clk)) {
 218                ret = PTR_ERR(ctx->clk);
 219                goto err_free;
 220        }
 221
 222        ret = clk_prepare(ctx->clk);
 223        if (ret < 0) {
 224                clk_put(ctx->clk);
 225                goto err_free;
 226        }
 227
 228        return ctx;
 229
 230err_free:
 231        kfree(ctx);
 232
 233        return ERR_PTR(ret);
 234}
 235
 236/**
 237 * regmap_init_mmio_clk(): Initialise register map with register clock
 238 *
 239 * @dev: Device that will be interacted with
 240 * @clk_id: register clock consumer ID
 241 * @regs: Pointer to memory-mapped IO region
 242 * @config: Configuration for register map
 243 *
 244 * The return value will be an ERR_PTR() on error or a valid pointer to
 245 * a struct regmap.
 246 */
 247struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 248                                    void __iomem *regs,
 249                                    const struct regmap_config *config)
 250{
 251        struct regmap_mmio_context *ctx;
 252
 253        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 254        if (IS_ERR(ctx))
 255                return ERR_CAST(ctx);
 256
 257        return regmap_init(dev, &regmap_mmio, ctx, config);
 258}
 259EXPORT_SYMBOL_GPL(regmap_init_mmio_clk);
 260
 261/**
 262 * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
 263 *
 264 * @dev: Device that will be interacted with
 265 * @clk_id: register clock consumer ID
 266 * @regs: Pointer to memory-mapped IO region
 267 * @config: Configuration for register map
 268 *
 269 * The return value will be an ERR_PTR() on error or a valid pointer
 270 * to a struct regmap.  The regmap will be automatically freed by the
 271 * device management code.
 272 */
 273struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 274                                         void __iomem *regs,
 275                                         const struct regmap_config *config)
 276{
 277        struct regmap_mmio_context *ctx;
 278
 279        ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
 280        if (IS_ERR(ctx))
 281                return ERR_CAST(ctx);
 282
 283        return devm_regmap_init(dev, &regmap_mmio, ctx, config);
 284}
 285EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
 286
 287MODULE_LICENSE("GPL v2");
 288