134 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2015, National Instruments Corp.4 *5 * Xilinx Zynq Reset controller driver6 *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 98 priv->slcr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,99 "syscon");100 if (IS_ERR(priv->slcr)) {101 dev_err(&pdev->dev, "unable to get zynq-slcr regmap");102 return PTR_ERR(priv->slcr);103 }104 105 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);106 if (!res) {107 dev_err(&pdev->dev, "missing IO resource\n");108 return -ENODEV;109 }110 111 priv->offset = res->start;112 113 priv->rcdev.owner = THIS_MODULE;114 priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG;115 priv->rcdev.ops = &zynq_reset_ops;116 priv->rcdev.of_node = pdev->dev.of_node;117 118 return devm_reset_controller_register(&pdev->dev, &priv->rcdev);119}120 121static const struct of_device_id zynq_reset_dt_ids[] = {122 { .compatible = "xlnx,zynq-reset", },123 { /* sentinel */ },124};125 126static struct platform_driver zynq_reset_driver = {127 .probe = zynq_reset_probe,128 .driver = {129 .name = KBUILD_MODNAME,130 .of_match_table = zynq_reset_dt_ids,131 },132};133builtin_platform_driver(zynq_reset_driver);134