linux/drivers/soc/sunxi/sunxi_sram.c
<<
>>
Prefs
   1/*
   2 * Allwinner SoCs SRAM Controller Driver
   3 *
   4 * Copyright (C) 2015 Maxime Ripard
   5 *
   6 * Author: Maxime Ripard <maxime.ripard@free-electrons.com>
   7 *
   8 * This file is licensed under the terms of the GNU General Public
   9 * License version 2.  This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13#include <linux/debugfs.h>
  14#include <linux/io.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_address.h>
  18#include <linux/of_device.h>
  19#include <linux/platform_device.h>
  20
  21#include <linux/soc/sunxi/sunxi_sram.h>
  22
  23struct sunxi_sram_func {
  24        char    *func;
  25        u8      val;
  26};
  27
  28struct sunxi_sram_data {
  29        char                    *name;
  30        u8                      reg;
  31        u8                      offset;
  32        u8                      width;
  33        struct sunxi_sram_func  *func;
  34        struct list_head        list;
  35};
  36
  37struct sunxi_sram_desc {
  38        struct sunxi_sram_data  data;
  39        bool                    claimed;
  40};
  41
  42#define SUNXI_SRAM_MAP(_val, _func)                             \
  43        {                                                       \
  44                .func = _func,                                  \
  45                .val = _val,                                    \
  46        }
  47
  48#define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...)         \
  49        {                                                       \
  50                .name = _name,                                  \
  51                .reg = _reg,                                    \
  52                .offset = _off,                                 \
  53                .width = _width,                                \
  54                .func = (struct sunxi_sram_func[]){             \
  55                        __VA_ARGS__, { } },                     \
  56        }
  57
  58static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = {
  59        .data   = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2,
  60                                  SUNXI_SRAM_MAP(0, "cpu"),
  61                                  SUNXI_SRAM_MAP(1, "emac")),
  62};
  63
  64static struct sunxi_sram_desc sun4i_a10_sram_d = {
  65        .data   = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1,
  66                                  SUNXI_SRAM_MAP(0, "cpu"),
  67                                  SUNXI_SRAM_MAP(1, "usb-otg")),
  68};
  69
  70static const struct of_device_id sunxi_sram_dt_ids[] = {
  71        {
  72                .compatible     = "allwinner,sun4i-a10-sram-a3-a4",
  73                .data           = &sun4i_a10_sram_a3_a4.data,
  74        },
  75        {
  76                .compatible     = "allwinner,sun4i-a10-sram-d",
  77                .data           = &sun4i_a10_sram_d.data,
  78        },
  79        {}
  80};
  81
  82static struct device *sram_dev;
  83static LIST_HEAD(claimed_sram);
  84static DEFINE_SPINLOCK(sram_lock);
  85static void __iomem *base;
  86
  87static int sunxi_sram_show(struct seq_file *s, void *data)
  88{
  89        struct device_node *sram_node, *section_node;
  90        const struct sunxi_sram_data *sram_data;
  91        const struct of_device_id *match;
  92        struct sunxi_sram_func *func;
  93        const __be32 *sram_addr_p, *section_addr_p;
  94        u32 val;
  95
  96        seq_puts(s, "Allwinner sunXi SRAM\n");
  97        seq_puts(s, "--------------------\n\n");
  98
  99        for_each_child_of_node(sram_dev->of_node, sram_node) {
 100                sram_addr_p = of_get_address(sram_node, 0, NULL, NULL);
 101
 102                seq_printf(s, "sram@%08x\n",
 103                           be32_to_cpu(*sram_addr_p));
 104
 105                for_each_child_of_node(sram_node, section_node) {
 106                        match = of_match_node(sunxi_sram_dt_ids, section_node);
 107                        if (!match)
 108                                continue;
 109                        sram_data = match->data;
 110
 111                        section_addr_p = of_get_address(section_node, 0,
 112                                                        NULL, NULL);
 113
 114                        seq_printf(s, "\tsection@%04x\t(%s)\n",
 115                                   be32_to_cpu(*section_addr_p),
 116                                   sram_data->name);
 117
 118                        val = readl(base + sram_data->reg);
 119                        val >>= sram_data->offset;
 120                        val &= GENMASK(sram_data->width - 1, 0);
 121
 122                        for (func = sram_data->func; func->func; func++) {
 123                                seq_printf(s, "\t\t%s%c\n", func->func,
 124                                           func->val == val ? '*' : ' ');
 125                        }
 126                }
 127
 128                seq_puts(s, "\n");
 129        }
 130
 131        return 0;
 132}
 133
 134static int sunxi_sram_open(struct inode *inode, struct file *file)
 135{
 136        return single_open(file, sunxi_sram_show, inode->i_private);
 137}
 138
 139static const struct file_operations sunxi_sram_fops = {
 140        .open = sunxi_sram_open,
 141        .read = seq_read,
 142        .llseek = seq_lseek,
 143        .release = single_release,
 144};
 145
 146static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data)
 147{
 148        return container_of(data, struct sunxi_sram_desc, data);
 149}
 150
 151static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node,
 152                                                         unsigned int *value)
 153{
 154        const struct of_device_id *match;
 155        struct of_phandle_args args;
 156        int ret;
 157
 158        ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0,
 159                                               &args);
 160        if (ret)
 161                return ERR_PTR(ret);
 162
 163        if (!of_device_is_available(args.np)) {
 164                ret = -EBUSY;
 165                goto err;
 166        }
 167
 168        if (value)
 169                *value = args.args[0];
 170
 171        match = of_match_node(sunxi_sram_dt_ids, args.np);
 172        if (!match) {
 173                ret = -EINVAL;
 174                goto err;
 175        }
 176
 177        of_node_put(args.np);
 178        return match->data;
 179
 180err:
 181        of_node_put(args.np);
 182        return ERR_PTR(ret);
 183}
 184
 185int sunxi_sram_claim(struct device *dev)
 186{
 187        const struct sunxi_sram_data *sram_data;
 188        struct sunxi_sram_desc *sram_desc;
 189        unsigned int device;
 190        u32 val, mask;
 191
 192        if (IS_ERR(base))
 193                return -EPROBE_DEFER;
 194
 195        if (!dev || !dev->of_node)
 196                return -EINVAL;
 197
 198        sram_data = sunxi_sram_of_parse(dev->of_node, &device);
 199        if (IS_ERR(sram_data))
 200                return PTR_ERR(sram_data);
 201
 202        sram_desc = to_sram_desc(sram_data);
 203
 204        spin_lock(&sram_lock);
 205
 206        if (sram_desc->claimed) {
 207                spin_unlock(&sram_lock);
 208                return -EBUSY;
 209        }
 210
 211        mask = GENMASK(sram_data->offset + sram_data->width - 1,
 212                       sram_data->offset);
 213        val = readl(base + sram_data->reg);
 214        val &= ~mask;
 215        writel(val | ((device << sram_data->offset) & mask),
 216               base + sram_data->reg);
 217
 218        spin_unlock(&sram_lock);
 219
 220        return 0;
 221}
 222EXPORT_SYMBOL(sunxi_sram_claim);
 223
 224int sunxi_sram_release(struct device *dev)
 225{
 226        const struct sunxi_sram_data *sram_data;
 227        struct sunxi_sram_desc *sram_desc;
 228
 229        if (!dev || !dev->of_node)
 230                return -EINVAL;
 231
 232        sram_data = sunxi_sram_of_parse(dev->of_node, NULL);
 233        if (IS_ERR(sram_data))
 234                return -EINVAL;
 235
 236        sram_desc = to_sram_desc(sram_data);
 237
 238        spin_lock(&sram_lock);
 239        sram_desc->claimed = false;
 240        spin_unlock(&sram_lock);
 241
 242        return 0;
 243}
 244EXPORT_SYMBOL(sunxi_sram_release);
 245
 246static int sunxi_sram_probe(struct platform_device *pdev)
 247{
 248        struct resource *res;
 249        struct dentry *d;
 250
 251        sram_dev = &pdev->dev;
 252
 253        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 254        base = devm_ioremap_resource(&pdev->dev, res);
 255        if (IS_ERR(base))
 256                return PTR_ERR(base);
 257
 258        of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
 259
 260        d = debugfs_create_file("sram", S_IRUGO, NULL, NULL,
 261                                &sunxi_sram_fops);
 262        if (!d)
 263                return -ENOMEM;
 264
 265        return 0;
 266}
 267
 268static const struct of_device_id sunxi_sram_dt_match[] = {
 269        { .compatible = "allwinner,sun4i-a10-sram-controller" },
 270        { },
 271};
 272MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);
 273
 274static struct platform_driver sunxi_sram_driver = {
 275        .driver = {
 276                .name           = "sunxi-sram",
 277                .of_match_table = sunxi_sram_dt_match,
 278        },
 279        .probe  = sunxi_sram_probe,
 280};
 281module_platform_driver(sunxi_sram_driver);
 282
 283MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 284MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver");
 285MODULE_LICENSE("GPL");
 286