reset-zynq.c (3284B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2015, National Instruments Corp. 4 * 5 * Xilinx Zynq Reset controller driver 6 * 7 * Author: Moritz Fischer <moritz.fischer@ettus.com> 8 */ 9 10#include <linux/err.h> 11#include <linux/io.h> 12#include <linux/init.h> 13#include <linux/mfd/syscon.h> 14#include <linux/of.h> 15#include <linux/platform_device.h> 16#include <linux/reset-controller.h> 17#include <linux/regmap.h> 18#include <linux/types.h> 19 20struct zynq_reset_data { 21 struct regmap *slcr; 22 struct reset_controller_dev rcdev; 23 u32 offset; 24}; 25 26#define to_zynq_reset_data(p) \ 27 container_of((p), struct zynq_reset_data, rcdev) 28 29static int zynq_reset_assert(struct reset_controller_dev *rcdev, 30 unsigned long id) 31{ 32 struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); 33 34 int bank = id / BITS_PER_LONG; 35 int offset = id % BITS_PER_LONG; 36 37 pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, 38 bank, offset); 39 40 return regmap_update_bits(priv->slcr, 41 priv->offset + (bank * 4), 42 BIT(offset), 43 BIT(offset)); 44} 45 46static int zynq_reset_deassert(struct reset_controller_dev *rcdev, 47 unsigned long id) 48{ 49 struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); 50 51 int bank = id / BITS_PER_LONG; 52 int offset = id % BITS_PER_LONG; 53 54 pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, 55 bank, offset); 56 57 return regmap_update_bits(priv->slcr, 58 priv->offset + (bank * 4), 59 BIT(offset), 60 ~BIT(offset)); 61} 62 63static int zynq_reset_status(struct reset_controller_dev *rcdev, 64 unsigned long id) 65{ 66 struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); 67 68 int bank = id / BITS_PER_LONG; 69 int offset = id % BITS_PER_LONG; 70 int ret; 71 u32 reg; 72 73 pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, 74 bank, offset); 75 76 ret = regmap_read(priv->slcr, priv->offset + (bank * 4), ®); 77 if (ret) 78 return ret; 79 80 return !!(reg & BIT(offset)); 81} 82 83static const struct reset_control_ops zynq_reset_ops = { 84 .assert = zynq_reset_assert, 85 .deassert = zynq_reset_deassert, 86 .status = zynq_reset_status, 87}; 88 89static int zynq_reset_probe(struct platform_device *pdev) 90{ 91 struct resource *res; 92 struct zynq_reset_data *priv; 93 94 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 95 if (!priv) 96 return -ENOMEM; 97 platform_set_drvdata(pdev, priv); 98 99 priv->slcr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 100 "syscon"); 101 if (IS_ERR(priv->slcr)) { 102 dev_err(&pdev->dev, "unable to get zynq-slcr regmap"); 103 return PTR_ERR(priv->slcr); 104 } 105 106 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 107 if (!res) { 108 dev_err(&pdev->dev, "missing IO resource\n"); 109 return -ENODEV; 110 } 111 112 priv->offset = res->start; 113 114 priv->rcdev.owner = THIS_MODULE; 115 priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG; 116 priv->rcdev.ops = &zynq_reset_ops; 117 priv->rcdev.of_node = pdev->dev.of_node; 118 119 return devm_reset_controller_register(&pdev->dev, &priv->rcdev); 120} 121 122static const struct of_device_id zynq_reset_dt_ids[] = { 123 { .compatible = "xlnx,zynq-reset", }, 124 { /* sentinel */ }, 125}; 126 127static struct platform_driver zynq_reset_driver = { 128 .probe = zynq_reset_probe, 129 .driver = { 130 .name = KBUILD_MODNAME, 131 .of_match_table = zynq_reset_dt_ids, 132 }, 133}; 134builtin_platform_driver(zynq_reset_driver);