| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * (C) Copyright 2017 Rockchip Electronics Co., Ltd | |
| */ | |
| #include <dm.h> | |
| #include <log.h> | |
| #include <malloc.h> | |
| #include <reset-uclass.h> | |
| #include <linux/bitops.h> | |
| #include <linux/io.h> | |
| #include <asm/arch-rockchip/hardware.h> | |
| #include <dm/device-internal.h> | |
| #include <dm/lists.h> | |
| /* | |
| * Each reg has 16 bits reset signal for devices | |
| * Note: Not including rk2818 and older SoCs | |
| */ | |
| #define ROCKCHIP_RESET_NUM_IN_REG 16 | |
| struct rockchip_reset_priv { | |
| void __iomem *base; | |
| const int *lut; | |
| /* Rockchip reset reg locate at cru controller */ | |
| u32 reset_reg_offset; | |
| /* Rockchip reset reg number */ | |
| u32 reset_reg_num; | |
| }; | |
| static int rockchip_reset_request(struct reset_ctl *reset_ctl) | |
| { | |
| struct rockchip_reset_priv *priv = dev_get_priv(reset_ctl->dev); | |
| unsigned long id = reset_ctl->id; | |
| if (priv->lut) | |
| id = priv->lut[id]; | |
| debug("%s(reset_ctl=%p) (dev=%p, id=%lu) (reg_num=%d)\n", __func__, | |
| reset_ctl, reset_ctl->dev, id, priv->reset_reg_num); | |
| if (id / ROCKCHIP_RESET_NUM_IN_REG >= priv->reset_reg_num) | |
| return -EINVAL; | |
| return 0; | |
| } | |
| static int rockchip_reset_assert(struct reset_ctl *reset_ctl) | |
| { | |
| struct rockchip_reset_priv *priv = dev_get_priv(reset_ctl->dev); | |
| unsigned long id = reset_ctl->id; | |
| int bank, offset; | |
| if (priv->lut) | |
| id = priv->lut[id]; | |
| bank = id / ROCKCHIP_RESET_NUM_IN_REG; | |
| offset = id % ROCKCHIP_RESET_NUM_IN_REG; | |
| debug("%s(reset_ctl=%p) (dev=%p, id=%lu) (reg_addr=%p)\n", __func__, | |
| reset_ctl, reset_ctl->dev, id, priv->base + (bank * 4)); | |
| rk_setreg(priv->base + (bank * 4), BIT(offset)); | |
| return 0; | |
| } | |
| static int rockchip_reset_deassert(struct reset_ctl *reset_ctl) | |
| { | |
| struct rockchip_reset_priv *priv = dev_get_priv(reset_ctl->dev); | |
| unsigned long id = reset_ctl->id; | |
| int bank, offset; | |
| if (priv->lut) | |
| id = priv->lut[id]; | |
| bank = id / ROCKCHIP_RESET_NUM_IN_REG; | |
| offset = id % ROCKCHIP_RESET_NUM_IN_REG; | |
| debug("%s(reset_ctl=%p) (dev=%p, id=%lu) (reg_addr=%p)\n", __func__, | |
| reset_ctl, reset_ctl->dev, id, priv->base + (bank * 4)); | |
| rk_clrreg(priv->base + (bank * 4), BIT(offset)); | |
| return 0; | |
| } | |
| struct reset_ops rockchip_reset_ops = { | |
| .request = rockchip_reset_request, | |
| .rst_assert = rockchip_reset_assert, | |
| .rst_deassert = rockchip_reset_deassert, | |
| }; | |
| static int rockchip_reset_probe(struct udevice *dev) | |
| { | |
| struct rockchip_reset_priv *priv = dev_get_priv(dev); | |
| fdt_addr_t addr; | |
| fdt_size_t size; | |
| addr = dev_read_addr_size(dev, &size); | |
| if (addr == FDT_ADDR_T_NONE) | |
| return -EINVAL; | |
| if ((priv->reset_reg_offset == 0) && (priv->reset_reg_num == 0)) | |
| return -EINVAL; | |
| addr += priv->reset_reg_offset; | |
| priv->base = ioremap(addr, size); | |
| debug("%s(base=%p) (reg_offset=%x, reg_num=%d)\n", __func__, | |
| priv->base, priv->reset_reg_offset, priv->reset_reg_num); | |
| return 0; | |
| } | |
| int rockchip_reset_bind_lut(struct udevice *pdev, | |
| const int *lookup_table, | |
| u32 reg_offset, | |
| u32 reg_number) | |
| { | |
| struct udevice *rst_dev; | |
| struct rockchip_reset_priv *priv; | |
| int ret; | |
| ret = device_bind_driver_to_node(pdev, "rockchip_reset", "reset", | |
| dev_ofnode(pdev), &rst_dev); | |
| if (ret) { | |
| debug("Warning: No rockchip reset driver: ret=%d\n", ret); | |
| return ret; | |
| } | |
| priv = malloc(sizeof(struct rockchip_reset_priv)); | |
| priv->reset_reg_offset = reg_offset; | |
| priv->reset_reg_num = reg_number; | |
| priv->lut = lookup_table; | |
| dev_set_priv(rst_dev, priv); | |
| return 0; | |
| } | |
| int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number) | |
| { | |
| return rockchip_reset_bind_lut(pdev, NULL, reg_offset, reg_number); | |
| } | |
| U_BOOT_DRIVER(rockchip_reset) = { | |
| .name = "rockchip_reset", | |
| .id = UCLASS_RESET, | |
| .probe = rockchip_reset_probe, | |
| .ops = &rockchip_reset_ops, | |
| .priv_auto = sizeof(struct rockchip_reset_priv), | |
| }; |