|
@@ -0,0 +1,217 @@
|
|
|
+/*
|
|
|
+ * Register map access API - MMIO support
|
|
|
+ *
|
|
|
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
+ * more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/init.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/regmap.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+
|
|
|
+struct regmap_mmio_context {
|
|
|
+ void __iomem *regs;
|
|
|
+ unsigned val_bytes;
|
|
|
+};
|
|
|
+
|
|
|
+static int regmap_mmio_gather_write(void *context,
|
|
|
+ const void *reg, size_t reg_size,
|
|
|
+ const void *val, size_t val_size)
|
|
|
+{
|
|
|
+ struct regmap_mmio_context *ctx = context;
|
|
|
+ u32 offset;
|
|
|
+
|
|
|
+ if (reg_size != 4)
|
|
|
+ return -EIO;
|
|
|
+ if (val_size % ctx->val_bytes)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ offset = be32_to_cpup(reg);
|
|
|
+
|
|
|
+ while (val_size) {
|
|
|
+ switch (ctx->val_bytes) {
|
|
|
+ case 1:
|
|
|
+ writeb(*(u8 *)val, ctx->regs + offset);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ writew(be16_to_cpup(val), ctx->regs + offset);
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ writel(be32_to_cpup(val), ctx->regs + offset);
|
|
|
+ break;
|
|
|
+#ifdef CONFIG_64BIT
|
|
|
+ case 8:
|
|
|
+ writeq(be64_to_cpup(val), ctx->regs + offset);
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ /* Should be caught by regmap_mmio_check_config */
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ val_size -= ctx->val_bytes;
|
|
|
+ val += ctx->val_bytes;
|
|
|
+ offset += ctx->val_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int regmap_mmio_write(void *context, const void *data, size_t count)
|
|
|
+{
|
|
|
+ if (count < 4)
|
|
|
+ return -EIO;
|
|
|
+ return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
|
|
|
+}
|
|
|
+
|
|
|
+static int regmap_mmio_read(void *context,
|
|
|
+ const void *reg, size_t reg_size,
|
|
|
+ void *val, size_t val_size)
|
|
|
+{
|
|
|
+ struct regmap_mmio_context *ctx = context;
|
|
|
+ u32 offset;
|
|
|
+
|
|
|
+ if (reg_size != 4)
|
|
|
+ return -EIO;
|
|
|
+ if (val_size % ctx->val_bytes)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ offset = be32_to_cpup(reg);
|
|
|
+
|
|
|
+ while (val_size) {
|
|
|
+ switch (ctx->val_bytes) {
|
|
|
+ case 1:
|
|
|
+ *(u8 *)val = readb(ctx->regs + offset);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
|
|
|
+ break;
|
|
|
+#ifdef CONFIG_64BIT
|
|
|
+ case 8:
|
|
|
+ *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ /* Should be caught by regmap_mmio_check_config */
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ val_size -= ctx->val_bytes;
|
|
|
+ val += ctx->val_bytes;
|
|
|
+ offset += ctx->val_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void regmap_mmio_free_context(void *context)
|
|
|
+{
|
|
|
+ kfree(context);
|
|
|
+}
|
|
|
+
|
|
|
+static struct regmap_bus regmap_mmio = {
|
|
|
+ .fast_io = true,
|
|
|
+ .write = regmap_mmio_write,
|
|
|
+ .gather_write = regmap_mmio_gather_write,
|
|
|
+ .read = regmap_mmio_read,
|
|
|
+ .free_context = regmap_mmio_free_context,
|
|
|
+};
|
|
|
+
|
|
|
+struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
|
|
|
+ const struct regmap_config *config)
|
|
|
+{
|
|
|
+ struct regmap_mmio_context *ctx;
|
|
|
+
|
|
|
+ if (config->reg_bits != 32)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ if (config->pad_bits)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ switch (config->val_bits) {
|
|
|
+ case 8:
|
|
|
+ case 16:
|
|
|
+ case 32:
|
|
|
+#ifdef CONFIG_64BIT
|
|
|
+ case 64:
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx = kzalloc(GFP_KERNEL, sizeof(*ctx));
|
|
|
+ if (!ctx)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ ctx->regs = regs;
|
|
|
+ ctx->val_bytes = config->val_bits / 8;
|
|
|
+
|
|
|
+ return ctx;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * regmap_init_mmio(): Initialise register map
|
|
|
+ *
|
|
|
+ * @dev: Device that will be interacted with
|
|
|
+ * @regs: Pointer to memory-mapped IO region
|
|
|
+ * @config: Configuration for register map
|
|
|
+ *
|
|
|
+ * The return value will be an ERR_PTR() on error or a valid pointer to
|
|
|
+ * a struct regmap.
|
|
|
+ */
|
|
|
+struct regmap *regmap_init_mmio(struct device *dev,
|
|
|
+ void __iomem *regs,
|
|
|
+ const struct regmap_config *config)
|
|
|
+{
|
|
|
+ struct regmap_mmio_context *ctx;
|
|
|
+
|
|
|
+ ctx = regmap_mmio_gen_context(regs, config);
|
|
|
+ if (IS_ERR(ctx))
|
|
|
+ return ERR_CAST(ctx);
|
|
|
+
|
|
|
+ return regmap_init(dev, ®map_mmio, ctx, config);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(regmap_init_mmio);
|
|
|
+
|
|
|
+/**
|
|
|
+ * devm_regmap_init_mmio(): Initialise managed register map
|
|
|
+ *
|
|
|
+ * @dev: Device that will be interacted with
|
|
|
+ * @regs: Pointer to memory-mapped IO region
|
|
|
+ * @config: Configuration for register map
|
|
|
+ *
|
|
|
+ * The return value will be an ERR_PTR() on error or a valid pointer
|
|
|
+ * to a struct regmap. The regmap will be automatically freed by the
|
|
|
+ * device management code.
|
|
|
+ */
|
|
|
+struct regmap *devm_regmap_init_mmio(struct device *dev,
|
|
|
+ void __iomem *regs,
|
|
|
+ const struct regmap_config *config)
|
|
|
+{
|
|
|
+ struct regmap_mmio_context *ctx;
|
|
|
+
|
|
|
+ ctx = regmap_mmio_gen_context(regs, config);
|
|
|
+ if (IS_ERR(ctx))
|
|
|
+ return ERR_CAST(ctx);
|
|
|
+
|
|
|
+ return devm_regmap_init(dev, ®map_mmio, ctx, config);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
|
|
|
+
|
|
|
+MODULE_LICENSE("GPL v2");
|