linux/drivers/reset/reset-sunxi.c
<<
>>
Prefs
   1/*
   2 * Allwinner SoCs Reset Controller driver
   3 *
   4 * Copyright 2013 Maxime Ripard
   5 *
   6 * Maxime Ripard <maxime.ripard@free-electrons.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/err.h>
  15#include <linux/io.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/of_address.h>
  19#include <linux/platform_device.h>
  20#include <linux/reset-controller.h>
  21#include <linux/slab.h>
  22#include <linux/spinlock.h>
  23#include <linux/types.h>
  24
  25struct sunxi_reset_data {
  26        spinlock_t                      lock;
  27        void __iomem                    *membase;
  28        struct reset_controller_dev     rcdev;
  29};
  30
  31static int sunxi_reset_assert(struct reset_controller_dev *rcdev,
  32                              unsigned long id)
  33{
  34        struct sunxi_reset_data *data = container_of(rcdev,
  35                                                     struct sunxi_reset_data,
  36                                                     rcdev);
  37        int bank = id / BITS_PER_LONG;
  38        int offset = id % BITS_PER_LONG;
  39        unsigned long flags;
  40        u32 reg;
  41
  42        spin_lock_irqsave(&data->lock, flags);
  43
  44        reg = readl(data->membase + (bank * 4));
  45        writel(reg & ~BIT(offset), data->membase + (bank * 4));
  46
  47        spin_unlock_irqrestore(&data->lock, flags);
  48
  49        return 0;
  50}
  51
  52static int sunxi_reset_deassert(struct reset_controller_dev *rcdev,
  53                                unsigned long id)
  54{
  55        struct sunxi_reset_data *data = container_of(rcdev,
  56                                                     struct sunxi_reset_data,
  57                                                     rcdev);
  58        int bank = id / BITS_PER_LONG;
  59        int offset = id % BITS_PER_LONG;
  60        unsigned long flags;
  61        u32 reg;
  62
  63        spin_lock_irqsave(&data->lock, flags);
  64
  65        reg = readl(data->membase + (bank * 4));
  66        writel(reg | BIT(offset), data->membase + (bank * 4));
  67
  68        spin_unlock_irqrestore(&data->lock, flags);
  69
  70        return 0;
  71}
  72
  73static struct reset_control_ops sunxi_reset_ops = {
  74        .assert         = sunxi_reset_assert,
  75        .deassert       = sunxi_reset_deassert,
  76};
  77
  78static int sunxi_reset_init(struct device_node *np)
  79{
  80        struct sunxi_reset_data *data;
  81        struct resource res;
  82        resource_size_t size;
  83        int ret;
  84
  85        data = kzalloc(sizeof(*data), GFP_KERNEL);
  86        if (!data)
  87                return -ENOMEM;
  88
  89        ret = of_address_to_resource(np, 0, &res);
  90        if (ret)
  91                goto err_alloc;
  92
  93        size = resource_size(&res);
  94        if (!request_mem_region(res.start, size, np->name)) {
  95                ret = -EBUSY;
  96                goto err_alloc;
  97        }
  98
  99        data->membase = ioremap(res.start, size);
 100        if (!data->membase) {
 101                ret = -ENOMEM;
 102                goto err_alloc;
 103        }
 104
 105        spin_lock_init(&data->lock);
 106
 107        data->rcdev.owner = THIS_MODULE;
 108        data->rcdev.nr_resets = size * 32;
 109        data->rcdev.ops = &sunxi_reset_ops;
 110        data->rcdev.of_node = np;
 111        reset_controller_register(&data->rcdev);
 112
 113        return 0;
 114
 115err_alloc:
 116        kfree(data);
 117        return ret;
 118};
 119
 120/*
 121 * These are the reset controller we need to initialize early on in
 122 * our system, before we can even think of using a regular device
 123 * driver for it.
 124 */
 125static const struct of_device_id sunxi_early_reset_dt_ids[] __initdata = {
 126        { .compatible = "allwinner,sun6i-a31-ahb1-reset", },
 127        { /* sentinel */ },
 128};
 129
 130void __init sun6i_reset_init(void)
 131{
 132        struct device_node *np;
 133
 134        for_each_matching_node(np, sunxi_early_reset_dt_ids)
 135                sunxi_reset_init(np);
 136}
 137
 138/*
 139 * And these are the controllers we can register through the regular
 140 * device model.
 141 */
 142static const struct of_device_id sunxi_reset_dt_ids[] = {
 143         { .compatible = "allwinner,sun6i-a31-clock-reset", },
 144         { /* sentinel */ },
 145};
 146MODULE_DEVICE_TABLE(of, sunxi_reset_dt_ids);
 147
 148static int sunxi_reset_probe(struct platform_device *pdev)
 149{
 150        struct sunxi_reset_data *data;
 151        struct resource *res;
 152
 153        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 154        if (!data)
 155                return -ENOMEM;
 156
 157        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 158        data->membase = devm_ioremap_resource(&pdev->dev, res);
 159        if (IS_ERR(data->membase))
 160                return PTR_ERR(data->membase);
 161
 162        spin_lock_init(&data->lock);
 163
 164        data->rcdev.owner = THIS_MODULE;
 165        data->rcdev.nr_resets = resource_size(res) * 32;
 166        data->rcdev.ops = &sunxi_reset_ops;
 167        data->rcdev.of_node = pdev->dev.of_node;
 168
 169        return reset_controller_register(&data->rcdev);
 170}
 171
 172static int sunxi_reset_remove(struct platform_device *pdev)
 173{
 174        struct sunxi_reset_data *data = platform_get_drvdata(pdev);
 175
 176        reset_controller_unregister(&data->rcdev);
 177
 178        return 0;
 179}
 180
 181static struct platform_driver sunxi_reset_driver = {
 182        .probe  = sunxi_reset_probe,
 183        .remove = sunxi_reset_remove,
 184        .driver = {
 185                .name           = "sunxi-reset",
 186                .of_match_table = sunxi_reset_dt_ids,
 187        },
 188};
 189module_platform_driver(sunxi_reset_driver);
 190
 191MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
 192MODULE_DESCRIPTION("Allwinner SoCs Reset Controller Driver");
 193MODULE_LICENSE("GPL");
 194
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.