1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) STMicroelectronics 2018 - All Rights Reserved 4 * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics. 5 */ 6 7#include <linux/device.h> 8#include <linux/err.h> 9#include <linux/io.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/reset-controller.h> 13 14#define CLR_OFFSET 0x4 15 16struct stm32_reset_data { 17 struct reset_controller_dev rcdev; 18 void __iomem *membase; 19}; 20 21static inline struct stm32_reset_data * 22to_stm32_reset_data(struct reset_controller_dev *rcdev) 23{ 24 return container_of(rcdev, struct stm32_reset_data, rcdev); 25} 26 27static int stm32_reset_update(struct reset_controller_dev *rcdev, 28 unsigned long id, bool assert) 29{ 30 struct stm32_reset_data *data = to_stm32_reset_data(rcdev); 31 int reg_width = sizeof(u32); 32 int bank = id / (reg_width * BITS_PER_BYTE); 33 int offset = id % (reg_width * BITS_PER_BYTE); 34 void __iomem *addr; 35 36 addr = data->membase + (bank * reg_width); 37 if (!assert) 38 addr += CLR_OFFSET; 39 40 writel(BIT(offset), addr); 41 42 return 0; 43} 44 45static int stm32_reset_assert(struct reset_controller_dev *rcdev, 46 unsigned long id) 47{ 48 return stm32_reset_update(rcdev, id, true); 49} 50 51static int stm32_reset_deassert(struct reset_controller_dev *rcdev, 52 unsigned long id) 53{ 54 return stm32_reset_update(rcdev, id, false); 55} 56 57static int stm32_reset_status(struct reset_controller_dev *rcdev, 58 unsigned long id) 59{ 60 struct stm32_reset_data *data = to_stm32_reset_data(rcdev); 61 int reg_width = sizeof(u32); 62 int bank = id / (reg_width * BITS_PER_BYTE); 63 int offset = id % (reg_width * BITS_PER_BYTE); 64 u32 reg; 65 66 reg = readl(data->membase + (bank * reg_width)); 67 68 return !!(reg & BIT(offset)); 69} 70 71static const struct reset_control_ops stm32_reset_ops = { 72 .assert = stm32_reset_assert, 73 .deassert = stm32_reset_deassert, 74 .status = stm32_reset_status, 75}; 76 77static const struct of_device_id stm32_reset_dt_ids[] = { 78 { .compatible = "st,stm32mp1-rcc"}, 79 { /* sentinel */ }, 80}; 81 82static int stm32_reset_probe(struct platform_device *pdev) 83{ 84 struct device *dev = &pdev->dev; 85 struct stm32_reset_data *data; 86 void __iomem *membase; 87 struct resource *res; 88 89 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 90 if (!data) 91 return -ENOMEM; 92 93 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 94 membase = devm_ioremap_resource(dev, res); 95 if (IS_ERR(membase)) 96 return PTR_ERR(membase); 97 98 data->membase = membase; 99 data->rcdev.owner = THIS_MODULE; 100 data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE; 101 data->rcdev.ops = &stm32_reset_ops; 102 data->rcdev.of_node = dev->of_node; 103 104 return devm_reset_controller_register(dev, &data->rcdev); 105} 106 107static struct platform_driver stm32_reset_driver = { 108 .probe = stm32_reset_probe, 109 .driver = { 110 .name = "stm32mp1-reset", 111 .of_match_table = stm32_reset_dt_ids, 112 }, 113}; 114 115builtin_platform_driver(stm32_reset_driver); 116