linux/drivers/reset/reset-meson.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Amlogic Meson Reset Controller driver
   4 *
   5 * Copyright (c) 2016 BayLibre, SAS.
   6 * Author: Neil Armstrong <narmstrong@baylibre.com>
   7 */
   8#include <linux/err.h>
   9#include <linux/init.h>
  10#include <linux/io.h>
  11#include <linux/of.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/reset-controller.h>
  15#include <linux/slab.h>
  16#include <linux/types.h>
  17#include <linux/of_device.h>
  18
  19#define BITS_PER_REG    32
  20
  21struct meson_reset_param {
  22        int reg_count;
  23        int level_offset;
  24};
  25
  26struct meson_reset {
  27        void __iomem *reg_base;
  28        const struct meson_reset_param *param;
  29        struct reset_controller_dev rcdev;
  30        spinlock_t lock;
  31};
  32
  33static int meson_reset_reset(struct reset_controller_dev *rcdev,
  34                              unsigned long id)
  35{
  36        struct meson_reset *data =
  37                container_of(rcdev, struct meson_reset, rcdev);
  38        unsigned int bank = id / BITS_PER_REG;
  39        unsigned int offset = id % BITS_PER_REG;
  40        void __iomem *reg_addr = data->reg_base + (bank << 2);
  41
  42        writel(BIT(offset), reg_addr);
  43
  44        return 0;
  45}
  46
  47static int meson_reset_level(struct reset_controller_dev *rcdev,
  48                            unsigned long id, bool assert)
  49{
  50        struct meson_reset *data =
  51                container_of(rcdev, struct meson_reset, rcdev);
  52        unsigned int bank = id / BITS_PER_REG;
  53        unsigned int offset = id % BITS_PER_REG;
  54        void __iomem *reg_addr;
  55        unsigned long flags;
  56        u32 reg;
  57
  58        reg_addr = data->reg_base + data->param->level_offset + (bank << 2);
  59
  60        spin_lock_irqsave(&data->lock, flags);
  61
  62        reg = readl(reg_addr);
  63        if (assert)
  64                writel(reg & ~BIT(offset), reg_addr);
  65        else
  66                writel(reg | BIT(offset), reg_addr);
  67
  68        spin_unlock_irqrestore(&data->lock, flags);
  69
  70        return 0;
  71}
  72
  73static int meson_reset_assert(struct reset_controller_dev *rcdev,
  74                              unsigned long id)
  75{
  76        return meson_reset_level(rcdev, id, true);
  77}
  78
  79static int meson_reset_deassert(struct reset_controller_dev *rcdev,
  80                                unsigned long id)
  81{
  82        return meson_reset_level(rcdev, id, false);
  83}
  84
  85static const struct reset_control_ops meson_reset_ops = {
  86        .reset          = meson_reset_reset,
  87        .assert         = meson_reset_assert,
  88        .deassert       = meson_reset_deassert,
  89};
  90
  91static const struct meson_reset_param meson8b_param = {
  92        .reg_count      = 8,
  93        .level_offset   = 0x7c,
  94};
  95
  96static const struct meson_reset_param meson_a1_param = {
  97        .reg_count      = 3,
  98        .level_offset   = 0x40,
  99};
 100
 101static const struct of_device_id meson_reset_dt_ids[] = {
 102         { .compatible = "amlogic,meson8b-reset",    .data = &meson8b_param},
 103         { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
 104         { .compatible = "amlogic,meson-axg-reset",  .data = &meson8b_param},
 105         { .compatible = "amlogic,meson-a1-reset",   .data = &meson_a1_param},
 106         { /* sentinel */ },
 107};
 108MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
 109
 110static int meson_reset_probe(struct platform_device *pdev)
 111{
 112        struct meson_reset *data;
 113        struct resource *res;
 114
 115        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 116        if (!data)
 117                return -ENOMEM;
 118
 119        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 120        data->reg_base = devm_ioremap_resource(&pdev->dev, res);
 121        if (IS_ERR(data->reg_base))
 122                return PTR_ERR(data->reg_base);
 123
 124        data->param = of_device_get_match_data(&pdev->dev);
 125        if (!data->param)
 126                return -ENODEV;
 127
 128        platform_set_drvdata(pdev, data);
 129
 130        spin_lock_init(&data->lock);
 131
 132        data->rcdev.owner = THIS_MODULE;
 133        data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG;
 134        data->rcdev.ops = &meson_reset_ops;
 135        data->rcdev.of_node = pdev->dev.of_node;
 136
 137        return devm_reset_controller_register(&pdev->dev, &data->rcdev);
 138}
 139
 140static struct platform_driver meson_reset_driver = {
 141        .probe  = meson_reset_probe,
 142        .driver = {
 143                .name           = "meson_reset",
 144                .of_match_table = meson_reset_dt_ids,
 145        },
 146};
 147module_platform_driver(meson_reset_driver);
 148
 149MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
 150MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
 151MODULE_LICENSE("Dual BSD/GPL");
 152