1637cee5fSGabriel Fernandez // SPDX-License-Identifier: GPL-2.0 2637cee5fSGabriel Fernandez /* 3637cee5fSGabriel Fernandez * Copyright (C) STMicroelectronics 2022 - All Rights Reserved 4637cee5fSGabriel Fernandez * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. 5637cee5fSGabriel Fernandez */ 6637cee5fSGabriel Fernandez 7637cee5fSGabriel Fernandez #include <linux/of.h> 8637cee5fSGabriel Fernandez #include <linux/platform_device.h> 9637cee5fSGabriel Fernandez #include <linux/regmap.h> 10637cee5fSGabriel Fernandez #include <linux/reset-controller.h> 11637cee5fSGabriel Fernandez #include <linux/slab.h> 12637cee5fSGabriel Fernandez #include <linux/spinlock.h> 13637cee5fSGabriel Fernandez 1430500c2aSGabriel Fernandez #include "reset-stm32.h" 15637cee5fSGabriel Fernandez 16637cee5fSGabriel Fernandez struct stm32_reset_data { 17637cee5fSGabriel Fernandez /* reset lock */ 18637cee5fSGabriel Fernandez spinlock_t lock; 19637cee5fSGabriel Fernandez struct reset_controller_dev rcdev; 20637cee5fSGabriel Fernandez void __iomem *membase; 21637cee5fSGabriel Fernandez u32 clear_offset; 22*fd7a1c90SGabriel Fernandez const struct stm32_reset_cfg **reset_lines; 23637cee5fSGabriel Fernandez }; 24637cee5fSGabriel Fernandez 25637cee5fSGabriel Fernandez static inline struct stm32_reset_data * 26637cee5fSGabriel Fernandez to_stm32_reset_data(struct reset_controller_dev *rcdev) 27637cee5fSGabriel Fernandez { 28637cee5fSGabriel Fernandez return container_of(rcdev, struct stm32_reset_data, rcdev); 29637cee5fSGabriel Fernandez } 30637cee5fSGabriel Fernandez 31*fd7a1c90SGabriel Fernandez static const struct stm32_reset_cfg *stm32_get_reset_line(struct reset_controller_dev *rcdev, 32*fd7a1c90SGabriel Fernandez unsigned long id, 33*fd7a1c90SGabriel Fernandez struct stm32_reset_cfg *line) 34637cee5fSGabriel Fernandez { 35637cee5fSGabriel Fernandez struct stm32_reset_data *data = to_stm32_reset_data(rcdev); 36*fd7a1c90SGabriel Fernandez 37*fd7a1c90SGabriel Fernandez if (!data->reset_lines) { 38637cee5fSGabriel Fernandez int reg_width = sizeof(u32); 39637cee5fSGabriel Fernandez int bank = id / (reg_width * BITS_PER_BYTE); 40637cee5fSGabriel Fernandez int offset = id % (reg_width * BITS_PER_BYTE); 41637cee5fSGabriel Fernandez 42*fd7a1c90SGabriel Fernandez line->offset = bank * reg_width; 43*fd7a1c90SGabriel Fernandez line->bit_idx = offset; 44*fd7a1c90SGabriel Fernandez line->set_clr = (data->clear_offset ? true : false); 45*fd7a1c90SGabriel Fernandez 46*fd7a1c90SGabriel Fernandez return line; 47*fd7a1c90SGabriel Fernandez } 48*fd7a1c90SGabriel Fernandez 49*fd7a1c90SGabriel Fernandez return data->reset_lines[id]; 50*fd7a1c90SGabriel Fernandez } 51*fd7a1c90SGabriel Fernandez 52*fd7a1c90SGabriel Fernandez static int stm32_reset_update(struct reset_controller_dev *rcdev, 53*fd7a1c90SGabriel Fernandez unsigned long id, bool assert) 54*fd7a1c90SGabriel Fernandez { 55*fd7a1c90SGabriel Fernandez struct stm32_reset_data *data = to_stm32_reset_data(rcdev); 56*fd7a1c90SGabriel Fernandez struct stm32_reset_cfg line_reset; 57*fd7a1c90SGabriel Fernandez const struct stm32_reset_cfg *ptr_line; 58*fd7a1c90SGabriel Fernandez 59*fd7a1c90SGabriel Fernandez ptr_line = stm32_get_reset_line(rcdev, id, &line_reset); 60*fd7a1c90SGabriel Fernandez if (!ptr_line) 61*fd7a1c90SGabriel Fernandez return -EPERM; 62*fd7a1c90SGabriel Fernandez 63*fd7a1c90SGabriel Fernandez if (ptr_line->set_clr) { 64637cee5fSGabriel Fernandez void __iomem *addr; 65637cee5fSGabriel Fernandez 66*fd7a1c90SGabriel Fernandez addr = data->membase + ptr_line->offset; 67637cee5fSGabriel Fernandez if (!assert) 68637cee5fSGabriel Fernandez addr += data->clear_offset; 69637cee5fSGabriel Fernandez 70*fd7a1c90SGabriel Fernandez writel(BIT(ptr_line->bit_idx), addr); 71637cee5fSGabriel Fernandez 72637cee5fSGabriel Fernandez } else { 73637cee5fSGabriel Fernandez unsigned long flags; 74637cee5fSGabriel Fernandez u32 reg; 75637cee5fSGabriel Fernandez 76637cee5fSGabriel Fernandez spin_lock_irqsave(&data->lock, flags); 77637cee5fSGabriel Fernandez 78*fd7a1c90SGabriel Fernandez reg = readl(data->membase + ptr_line->offset); 79637cee5fSGabriel Fernandez 80637cee5fSGabriel Fernandez if (assert) 81*fd7a1c90SGabriel Fernandez reg |= BIT(ptr_line->bit_idx); 82637cee5fSGabriel Fernandez else 83*fd7a1c90SGabriel Fernandez reg &= ~BIT(ptr_line->bit_idx); 84637cee5fSGabriel Fernandez 85*fd7a1c90SGabriel Fernandez writel(reg, data->membase + ptr_line->offset); 86637cee5fSGabriel Fernandez 87637cee5fSGabriel Fernandez spin_unlock_irqrestore(&data->lock, flags); 88637cee5fSGabriel Fernandez } 89637cee5fSGabriel Fernandez 90637cee5fSGabriel Fernandez return 0; 91637cee5fSGabriel Fernandez } 92637cee5fSGabriel Fernandez 93637cee5fSGabriel Fernandez static int stm32_reset_assert(struct reset_controller_dev *rcdev, 94637cee5fSGabriel Fernandez unsigned long id) 95637cee5fSGabriel Fernandez { 96637cee5fSGabriel Fernandez return stm32_reset_update(rcdev, id, true); 97637cee5fSGabriel Fernandez } 98637cee5fSGabriel Fernandez 99637cee5fSGabriel Fernandez static int stm32_reset_deassert(struct reset_controller_dev *rcdev, 100637cee5fSGabriel Fernandez unsigned long id) 101637cee5fSGabriel Fernandez { 102637cee5fSGabriel Fernandez return stm32_reset_update(rcdev, id, false); 103637cee5fSGabriel Fernandez } 104637cee5fSGabriel Fernandez 105637cee5fSGabriel Fernandez static int stm32_reset_status(struct reset_controller_dev *rcdev, 106637cee5fSGabriel Fernandez unsigned long id) 107637cee5fSGabriel Fernandez { 108637cee5fSGabriel Fernandez struct stm32_reset_data *data = to_stm32_reset_data(rcdev); 109*fd7a1c90SGabriel Fernandez struct stm32_reset_cfg line_reset; 110*fd7a1c90SGabriel Fernandez const struct stm32_reset_cfg *ptr_line; 111637cee5fSGabriel Fernandez u32 reg; 112637cee5fSGabriel Fernandez 113*fd7a1c90SGabriel Fernandez ptr_line = stm32_get_reset_line(rcdev, id, &line_reset); 114*fd7a1c90SGabriel Fernandez if (!ptr_line) 115*fd7a1c90SGabriel Fernandez return -EPERM; 116637cee5fSGabriel Fernandez 117*fd7a1c90SGabriel Fernandez reg = readl(data->membase + ptr_line->offset); 118*fd7a1c90SGabriel Fernandez 119*fd7a1c90SGabriel Fernandez return !!(reg & BIT(ptr_line->bit_idx)); 120637cee5fSGabriel Fernandez } 121637cee5fSGabriel Fernandez 122637cee5fSGabriel Fernandez static const struct reset_control_ops stm32_reset_ops = { 123637cee5fSGabriel Fernandez .assert = stm32_reset_assert, 124637cee5fSGabriel Fernandez .deassert = stm32_reset_deassert, 125637cee5fSGabriel Fernandez .status = stm32_reset_status, 126637cee5fSGabriel Fernandez }; 127637cee5fSGabriel Fernandez 12830500c2aSGabriel Fernandez int stm32_rcc_reset_init(struct device *dev, struct clk_stm32_reset_data *data, 129637cee5fSGabriel Fernandez void __iomem *base) 130637cee5fSGabriel Fernandez { 13130500c2aSGabriel Fernandez struct stm32_reset_data *reset_data; 132637cee5fSGabriel Fernandez 133637cee5fSGabriel Fernandez reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 134637cee5fSGabriel Fernandez if (!reset_data) 135637cee5fSGabriel Fernandez return -ENOMEM; 136637cee5fSGabriel Fernandez 137a1ea0857SWei Yongjun spin_lock_init(&reset_data->lock); 13830500c2aSGabriel Fernandez 139637cee5fSGabriel Fernandez reset_data->membase = base; 140637cee5fSGabriel Fernandez reset_data->rcdev.owner = THIS_MODULE; 141637cee5fSGabriel Fernandez reset_data->rcdev.ops = &stm32_reset_ops; 142637cee5fSGabriel Fernandez reset_data->rcdev.of_node = dev_of_node(dev); 14330500c2aSGabriel Fernandez reset_data->rcdev.nr_resets = data->nr_lines; 144*fd7a1c90SGabriel Fernandez reset_data->reset_lines = data->reset_lines; 145637cee5fSGabriel Fernandez reset_data->clear_offset = data->clear_offset; 146637cee5fSGabriel Fernandez 147637cee5fSGabriel Fernandez return reset_controller_register(&reset_data->rcdev); 148637cee5fSGabriel Fernandez } 149