| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Amlogic Meson Reset Controller driver | |
| * | |
| * Copyright (c) 2018 BayLibre, SAS. | |
| * Author: Neil Armstrong <narmstrong@baylibre.com> | |
| */ | |
| #include <dm.h> | |
| #include <log.h> | |
| #include <malloc.h> | |
| #include <reset-uclass.h> | |
| #include <regmap.h> | |
| #include <linux/bitops.h> | |
| #include <linux/delay.h> | |
| #define BITS_PER_REG 32 | |
| struct meson_reset_drvdata { | |
| unsigned int reg_count; | |
| unsigned int level_offset; | |
| }; | |
| struct meson_reset_priv { | |
| struct regmap *regmap; | |
| struct meson_reset_drvdata *drvdata; | |
| }; | |
| static int meson_reset_request(struct reset_ctl *reset_ctl) | |
| { | |
| struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); | |
| struct meson_reset_drvdata *data = priv->drvdata; | |
| if (reset_ctl->id > (data->reg_count * BITS_PER_REG)) | |
| return -EINVAL; | |
| return 0; | |
| } | |
| static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert) | |
| { | |
| struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); | |
| struct meson_reset_drvdata *data = priv->drvdata; | |
| uint bank = reset_ctl->id / BITS_PER_REG; | |
| uint offset = reset_ctl->id % BITS_PER_REG; | |
| uint reg_offset = data->level_offset + (bank << 2); | |
| uint val; | |
| regmap_read(priv->regmap, reg_offset, &val); | |
| if (assert) | |
| val &= ~BIT(offset); | |
| else | |
| val |= BIT(offset); | |
| regmap_write(priv->regmap, reg_offset, val); | |
| return 0; | |
| } | |
| static int meson_reset_assert(struct reset_ctl *reset_ctl) | |
| { | |
| return meson_reset_level(reset_ctl, true); | |
| } | |
| static int meson_reset_deassert(struct reset_ctl *reset_ctl) | |
| { | |
| return meson_reset_level(reset_ctl, false); | |
| } | |
| struct reset_ops meson_reset_ops = { | |
| .request = meson_reset_request, | |
| .rst_assert = meson_reset_assert, | |
| .rst_deassert = meson_reset_deassert, | |
| }; | |
| static const struct meson_reset_drvdata meson_gxbb_data = { | |
| .reg_count = 8, | |
| .level_offset = 0x7c, | |
| }; | |
| static const struct meson_reset_drvdata meson_a1_data = { | |
| .reg_count = 3, | |
| .level_offset = 0x40, | |
| }; | |
| static const struct udevice_id meson_reset_ids[] = { | |
| { | |
| .compatible = "amlogic,meson-gxbb-reset", | |
| .data = (ulong)&meson_gxbb_data, | |
| }, | |
| { | |
| .compatible = "amlogic,meson-axg-reset", | |
| .data = (ulong)&meson_gxbb_data, | |
| }, | |
| { | |
| .compatible = "amlogic,meson-a1-reset", | |
| .data = (ulong)&meson_a1_data, | |
| }, | |
| { } | |
| }; | |
| static int meson_reset_probe(struct udevice *dev) | |
| { | |
| struct meson_reset_priv *priv = dev_get_priv(dev); | |
| priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev); | |
| return regmap_init_mem(dev_ofnode(dev), &priv->regmap); | |
| } | |
| U_BOOT_DRIVER(meson_reset) = { | |
| .name = "meson_reset", | |
| .id = UCLASS_RESET, | |
| .of_match = meson_reset_ids, | |
| .probe = meson_reset_probe, | |
| .ops = &meson_reset_ops, | |
| .priv_auto = sizeof(struct meson_reset_priv), | |
| }; |