cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

reset-syscfg.c (4910B)


      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}