diff options
Diffstat (limited to 'drivers/reset/sunxi/reset-sunxi.c')
-rw-r--r-- | drivers/reset/sunxi/reset-sunxi.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/drivers/reset/sunxi/reset-sunxi.c b/drivers/reset/sunxi/reset-sunxi.c new file mode 100644 index 0000000000..8fbfa85b91 --- /dev/null +++ b/drivers/reset/sunxi/reset-sunxi.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <reset-uclass.h> +#include <dm/device.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/sizes.h> + +#include "ccu_reset.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct sunxi_reset_priv { + void *base; + size_t size; +}; + +static int sunxi_reset_request(struct reset_ctl *reset_ctl) +{ + debug("%s (%s): id %ld\n", + reset_ctl->dev->name, __func__, reset_ctl->id); + return 0; +} + +static int sunxi_reset_free(struct reset_ctl *reset_ctl) +{ + debug("%s (%s): id %ld\n", + reset_ctl->dev->name, __func__, reset_ctl->id); + return 0; +} + +static int sunxi_reset_update_bit(struct udevice *dev, + const uint32_t off, + const uint32_t bitmask, + const bool assert) +{ + const struct sunxi_reset_priv *priv = dev_get_priv(dev); + + debug("%s (%s): base %p offset %x bit %x assert %d size %ld\n", + dev->name, __func__, + priv->base, off, bitmask, assert, priv->size); + + if (off >= priv->size) + return -EINVAL; + + if (assert) + clrbits_le32(priv->base + off, bitmask); + else + setbits_le32(priv->base + off, bitmask); + + return 0; +} + +static int sunxi_ccu_reset_update(struct reset_ctl *reset_ctl, bool assert) +{ + struct udevice *dev = reset_ctl->dev; + const struct ccu_reset_map *reset_map = + (const struct ccu_reset_map *)dev_get_driver_data(dev); + const struct ccu_reset_map *entry = &reset_map[reset_ctl->id]; + + return sunxi_reset_update_bit(dev, entry->reg, entry->bit, assert); +} + +static int sunxi_ccu_reset_assert(struct reset_ctl *reset_ctl) +{ + return sunxi_ccu_reset_update(reset_ctl, true); +} + +static int sunxi_ccu_reset_deassert(struct reset_ctl *reset_ctl) +{ + return sunxi_ccu_reset_update(reset_ctl, false); +} + +static int sunxi_reset_update(struct reset_ctl *reset_ctl, bool assert) +{ + struct udevice *dev = reset_ctl->dev; + const unsigned long id = reset_ctl->id; + const unsigned long offset = (id / 32) * sizeof(uint32_t); + const unsigned int bit = id % 32; + + return sunxi_reset_update_bit(dev, offset, BIT(bit), assert); +} + +static int sunxi_reset_assert(struct reset_ctl *reset_ctl) +{ + return sunxi_reset_update(reset_ctl, true); +} + +static int sunxi_reset_deassert(struct reset_ctl *reset_ctl) +{ + return sunxi_reset_update(reset_ctl, false); +} + +static int sunxi_reset_probe(struct udevice *dev) +{ + struct sunxi_reset_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset, + "reg", 0, &size, false); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: failed to find base address ('reg')\n", dev->name); + return -ENODEV; + } + priv->base = (void *)addr; + priv->size = size; + + if (!priv->base) + return -ENOMEM; + + return 0; +} + +static const struct reset_ops sunxi_reset_ops = { + .request = sunxi_reset_request, + .free = sunxi_reset_free, + .rst_assert = sunxi_reset_assert, + .rst_deassert = sunxi_reset_deassert, +}; + +static const struct udevice_id sunxi_reset_match[] = { + { .compatible = "allwinner,sun6i-a31-clock-reset" }, + { } +}; + + +U_BOOT_DRIVER(sunxi_reset) = { + .name = "sunxi-reset", + .id = UCLASS_RESET, + .of_match = sunxi_reset_match, + .ops = &sunxi_reset_ops, + .priv_auto_alloc_size = sizeof(struct sunxi_reset_priv), + .probe = sunxi_reset_probe, +}; + +static const struct reset_ops sunxi_ccu_reset_ops = { + .request = sunxi_reset_request, + .free = sunxi_reset_free, + .rst_assert = sunxi_ccu_reset_assert, + .rst_deassert = sunxi_ccu_reset_deassert, +}; + +#if defined(CONFIG_MACH_SUN50I) +extern const struct ccu_reset_map sun50i_a64_ccu_resets; +#endif + +static const struct udevice_id sunxi_ccu_reset_match[] = { +#if defined(CONFIG_MACH_SUN50I) + { .compatible = "allwinner,sun50i-a64-ccu", + .data = (ulong)&sun50i_a64_ccu_resets }, +#endif + { } +}; + +U_BOOT_DRIVER(sunxi_ccu_reset) = { + .name = "sunxi-ccu-reset", + .id = UCLASS_RESET, + .of_match = sunxi_ccu_reset_match, + .ops = &sunxi_ccu_reset_ops, + .priv_auto_alloc_size = sizeof(struct sunxi_reset_priv), + .probe = sunxi_reset_probe, +}; |