linux/drivers/reset/sti/reset-syscfg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2013 STMicroelectronics Limited
   4 * Author: Stephen Gallimore <stephen.gallimore@st.com>
   5 *
   6 * Inspired by mach-imx/src.c
   7 */
   8#include <linux/kernel.h>
   9#include <linux/platform_device.h>
  10#include <linux/module.h>
  11#include <linux/err.h>
  12#include <linux/types.h>
  13#include <linux/of_device.h>
  14#include <linux/regmap.h>
  15#include <linux/mfd/syscon.h>
  16
  17#include "reset-syscfg.h"
  18
  19/**
  20 * struct syscfg_reset_channel - Reset channel regmap configuration
  21 *
  22 * @reset: regmap field for the channel's reset bit.
  23 * @ack: regmap field for the channel's ack bit (optional).
  24 */
  25struct syscfg_reset_channel {
  26        struct regmap_field *reset;
  27        struct regmap_field *ack;
  28};
  29
  30/**
  31 * struct syscfg_reset_controller - A reset controller which groups together
  32 * a set of related reset bits, which may be located in different system
  33 * configuration registers.
  34 *
  35 * @rst: base reset controller structure.
  36 * @active_low: are the resets in this controller active low, i.e. clearing
  37 *              the reset bit puts the hardware into reset.
  38 * @channels: An array of reset channels for this controller.
  39 */
  40struct syscfg_reset_controller {
  41        struct reset_controller_dev rst;
  42        bool active_low;
  43        struct syscfg_reset_channel *channels;
  44};
  45
  46#define to_syscfg_reset_controller(_rst) \
  47        container_of(_rst, struct syscfg_reset_controller, rst)
  48
  49static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev,
  50                                   unsigned long idx, int assert)
  51{
  52        struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
  53        const struct syscfg_reset_channel *ch;
  54        u32 ctrl_val = rst->active_low ? !assert : !!assert;
  55        int err;
  56
  57        if (idx >= rcdev->nr_resets)
  58                return -EINVAL;
  59
  60        ch = &rst->channels[idx];
  61
  62        err = regmap_field_write(ch->reset, ctrl_val);
  63        if (err)
  64                return err;
  65
  66        if (ch->ack) {
  67                unsigned long timeout = jiffies + msecs_to_jiffies(1000);
  68                u32 ack_val;
  69
  70                while (true) {
  71                        err = regmap_field_read(ch->ack, &ack_val);
  72                        if (err)
  73                                return err;
  74
  75                        if (ack_val == ctrl_val)
  76                                break;
  77
  78                        if (time_after(jiffies, timeout))
  79                                return -ETIME;
  80
  81                        cpu_relax();
  82                }
  83        }
  84
  85        return 0;
  86}
  87
  88static int syscfg_reset_assert(struct reset_controller_dev *rcdev,
  89                               unsigned long idx)
  90{
  91        return syscfg_reset_program_hw(rcdev, idx, true);
  92}
  93
  94static int syscfg_reset_deassert(struct reset_controller_dev *rcdev,
  95                                 unsigned long idx)
  96{
  97        return syscfg_reset_program_hw(rcdev, idx, false);
  98}
  99
 100static int syscfg_reset_dev(struct reset_controller_dev *rcdev,
 101                            unsigned long idx)
 102{
 103        int err;
 104
 105        err = syscfg_reset_assert(rcdev, idx);
 106        if (err)
 107                return err;
 108
 109        return syscfg_reset_deassert(rcdev, idx);
 110}
 111
 112static int syscfg_reset_status(struct reset_controller_dev *rcdev,
 113                               unsigned long idx)
 114{
 115        struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
 116        const struct syscfg_reset_channel *ch;
 117        u32 ret_val = 0;
 118        int err;
 119
 120        if (idx >= rcdev->nr_resets)
 121                return -EINVAL;
 122
 123        ch = &rst->channels[idx];
 124        if (ch->ack)
 125                err = regmap_field_read(ch->ack, &ret_val);
 126        else
 127                err = regmap_field_read(ch->reset, &ret_val);
 128        if (err)
 129                return err;
 130
 131        return rst->active_low ? !ret_val : !!ret_val;
 132}
 133
 134static const struct reset_control_ops syscfg_reset_ops = {
 135        .reset    = syscfg_reset_dev,
 136        .assert   = syscfg_reset_assert,
 137        .deassert = syscfg_reset_deassert,
 138        .status   = syscfg_reset_status,
 139};
 140
 141static int syscfg_reset_controller_register(struct device *dev,
 142                                const struct syscfg_reset_controller_data *data)
 143{
 144        struct syscfg_reset_controller *rc;
 145        int i, err;
 146
 147        rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
 148        if (!rc)
 149                return -ENOMEM;
 150
 151        rc->channels = devm_kcalloc(dev, data->nr_channels,
 152                                    sizeof(*rc->channels), GFP_KERNEL);
 153        if (!rc->channels)
 154                return -ENOMEM;
 155
 156        rc->rst.ops = &syscfg_reset_ops;
 157        rc->rst.of_node = dev->of_node;
 158        rc->rst.nr_resets = data->nr_channels;
 159        rc->active_low = data->active_low;
 160
 161        for (i = 0; i < data->nr_channels; i++) {
 162                struct regmap *map;
 163                struct regmap_field *f;
 164                const char *compatible = data->channels[i].compatible;
 165
 166                map = syscon_regmap_lookup_by_compatible(compatible);
 167                if (IS_ERR(map))
 168                        return PTR_ERR(map);
 169
 170                f = devm_regmap_field_alloc(dev, map, data->channels[i].reset);
 171                if (IS_ERR(f))
 172                        return PTR_ERR(f);
 173
 174                rc->channels[i].reset = f;
 175
 176                if (!data->wait_for_ack)
 177                        continue;
 178
 179                f = devm_regmap_field_alloc(dev, map, data->channels[i].ack);
 180                if (IS_ERR(f))
 181                        return PTR_ERR(f);
 182
 183                rc->channels[i].ack = f;
 184        }
 185
 186        err = reset_controller_register(&rc->rst);
 187        if (!err)
 188                dev_info(dev, "registered\n");
 189
 190        return err;
 191}
 192
 193int syscfg_reset_probe(struct platform_device *pdev)
 194{
 195        struct device *dev = pdev ? &pdev->dev : NULL;
 196        const struct of_device_id *match;
 197
 198        if (!dev || !dev->driver)
 199                return -ENODEV;
 200
 201        match = of_match_device(dev->driver->of_match_table, dev);
 202        if (!match || !match->data)
 203                return -EINVAL;
 204
 205        return syscfg_reset_controller_register(dev, match->data);
 206}
 207