12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 281c22ad0SPhilipp Zabel /* 381c22ad0SPhilipp Zabel * Simple Reset Controller Driver 481c22ad0SPhilipp Zabel * 581c22ad0SPhilipp Zabel * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de> 681c22ad0SPhilipp Zabel * 781c22ad0SPhilipp Zabel * Based on Allwinner SoCs Reset Controller driver 881c22ad0SPhilipp Zabel * 981c22ad0SPhilipp Zabel * Copyright 2013 Maxime Ripard 1081c22ad0SPhilipp Zabel * 1181c22ad0SPhilipp Zabel * Maxime Ripard <maxime.ripard@free-electrons.com> 1281c22ad0SPhilipp Zabel */ 1381c22ad0SPhilipp Zabel 14*a9701376SMaxime Ripard #include <linux/delay.h> 1581c22ad0SPhilipp Zabel #include <linux/device.h> 1681c22ad0SPhilipp Zabel #include <linux/err.h> 1781c22ad0SPhilipp Zabel #include <linux/io.h> 1881c22ad0SPhilipp Zabel #include <linux/of.h> 1981c22ad0SPhilipp Zabel #include <linux/of_device.h> 2081c22ad0SPhilipp Zabel #include <linux/platform_device.h> 2181c22ad0SPhilipp Zabel #include <linux/reset-controller.h> 229357b046SMaxime Ripard #include <linux/reset/reset-simple.h> 2381c22ad0SPhilipp Zabel #include <linux/spinlock.h> 2481c22ad0SPhilipp Zabel 2581c22ad0SPhilipp Zabel static inline struct reset_simple_data * 2681c22ad0SPhilipp Zabel to_reset_simple_data(struct reset_controller_dev *rcdev) 2781c22ad0SPhilipp Zabel { 2881c22ad0SPhilipp Zabel return container_of(rcdev, struct reset_simple_data, rcdev); 2981c22ad0SPhilipp Zabel } 3081c22ad0SPhilipp Zabel 3181c22ad0SPhilipp Zabel static int reset_simple_update(struct reset_controller_dev *rcdev, 3281c22ad0SPhilipp Zabel unsigned long id, bool assert) 3381c22ad0SPhilipp Zabel { 3481c22ad0SPhilipp Zabel struct reset_simple_data *data = to_reset_simple_data(rcdev); 3581c22ad0SPhilipp Zabel int reg_width = sizeof(u32); 3681c22ad0SPhilipp Zabel int bank = id / (reg_width * BITS_PER_BYTE); 3781c22ad0SPhilipp Zabel int offset = id % (reg_width * BITS_PER_BYTE); 3881c22ad0SPhilipp Zabel unsigned long flags; 3981c22ad0SPhilipp Zabel u32 reg; 4081c22ad0SPhilipp Zabel 4181c22ad0SPhilipp Zabel spin_lock_irqsave(&data->lock, flags); 4281c22ad0SPhilipp Zabel 4381c22ad0SPhilipp Zabel reg = readl(data->membase + (bank * reg_width)); 4481c22ad0SPhilipp Zabel if (assert ^ data->active_low) 4581c22ad0SPhilipp Zabel reg |= BIT(offset); 4681c22ad0SPhilipp Zabel else 4781c22ad0SPhilipp Zabel reg &= ~BIT(offset); 4881c22ad0SPhilipp Zabel writel(reg, data->membase + (bank * reg_width)); 4981c22ad0SPhilipp Zabel 5081c22ad0SPhilipp Zabel spin_unlock_irqrestore(&data->lock, flags); 5181c22ad0SPhilipp Zabel 5281c22ad0SPhilipp Zabel return 0; 5381c22ad0SPhilipp Zabel } 5481c22ad0SPhilipp Zabel 5581c22ad0SPhilipp Zabel static int reset_simple_assert(struct reset_controller_dev *rcdev, 5681c22ad0SPhilipp Zabel unsigned long id) 5781c22ad0SPhilipp Zabel { 5881c22ad0SPhilipp Zabel return reset_simple_update(rcdev, id, true); 5981c22ad0SPhilipp Zabel } 6081c22ad0SPhilipp Zabel 6181c22ad0SPhilipp Zabel static int reset_simple_deassert(struct reset_controller_dev *rcdev, 6281c22ad0SPhilipp Zabel unsigned long id) 6381c22ad0SPhilipp Zabel { 6481c22ad0SPhilipp Zabel return reset_simple_update(rcdev, id, false); 6581c22ad0SPhilipp Zabel } 6681c22ad0SPhilipp Zabel 67*a9701376SMaxime Ripard static int reset_simple_reset(struct reset_controller_dev *rcdev, 68*a9701376SMaxime Ripard unsigned long id) 69*a9701376SMaxime Ripard { 70*a9701376SMaxime Ripard struct reset_simple_data *data = to_reset_simple_data(rcdev); 71*a9701376SMaxime Ripard int ret; 72*a9701376SMaxime Ripard 73*a9701376SMaxime Ripard if (!data->reset_us) 74*a9701376SMaxime Ripard return -ENOTSUPP; 75*a9701376SMaxime Ripard 76*a9701376SMaxime Ripard ret = reset_simple_assert(rcdev, id); 77*a9701376SMaxime Ripard if (ret) 78*a9701376SMaxime Ripard return ret; 79*a9701376SMaxime Ripard 80*a9701376SMaxime Ripard usleep_range(data->reset_us, data->reset_us * 2); 81*a9701376SMaxime Ripard 82*a9701376SMaxime Ripard return reset_simple_deassert(rcdev, id); 83*a9701376SMaxime Ripard } 84*a9701376SMaxime Ripard 85adf20d7cSPhilipp Zabel static int reset_simple_status(struct reset_controller_dev *rcdev, 86adf20d7cSPhilipp Zabel unsigned long id) 87adf20d7cSPhilipp Zabel { 88adf20d7cSPhilipp Zabel struct reset_simple_data *data = to_reset_simple_data(rcdev); 89adf20d7cSPhilipp Zabel int reg_width = sizeof(u32); 90adf20d7cSPhilipp Zabel int bank = id / (reg_width * BITS_PER_BYTE); 91adf20d7cSPhilipp Zabel int offset = id % (reg_width * BITS_PER_BYTE); 92adf20d7cSPhilipp Zabel u32 reg; 93adf20d7cSPhilipp Zabel 94adf20d7cSPhilipp Zabel reg = readl(data->membase + (bank * reg_width)); 95adf20d7cSPhilipp Zabel 96adf20d7cSPhilipp Zabel return !(reg & BIT(offset)) ^ !data->status_active_low; 97adf20d7cSPhilipp Zabel } 98adf20d7cSPhilipp Zabel 9981c22ad0SPhilipp Zabel const struct reset_control_ops reset_simple_ops = { 10081c22ad0SPhilipp Zabel .assert = reset_simple_assert, 10181c22ad0SPhilipp Zabel .deassert = reset_simple_deassert, 102*a9701376SMaxime Ripard .reset = reset_simple_reset, 103adf20d7cSPhilipp Zabel .status = reset_simple_status, 10481c22ad0SPhilipp Zabel }; 1059ad39ab2SKunihiko Hayashi EXPORT_SYMBOL_GPL(reset_simple_ops); 10681c22ad0SPhilipp Zabel 10781c22ad0SPhilipp Zabel /** 10881c22ad0SPhilipp Zabel * struct reset_simple_devdata - simple reset controller properties 109adf20d7cSPhilipp Zabel * @reg_offset: offset between base address and first reset register. 110adf20d7cSPhilipp Zabel * @nr_resets: number of resets. If not set, default to resource size in bits. 11181c22ad0SPhilipp Zabel * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits 11281c22ad0SPhilipp Zabel * are set to assert the reset. 113adf20d7cSPhilipp Zabel * @status_active_low: if true, bits read back as cleared while the reset is 114adf20d7cSPhilipp Zabel * asserted. Otherwise, bits read back as set while the 115adf20d7cSPhilipp Zabel * reset is asserted. 11681c22ad0SPhilipp Zabel */ 11781c22ad0SPhilipp Zabel struct reset_simple_devdata { 118adf20d7cSPhilipp Zabel u32 reg_offset; 119adf20d7cSPhilipp Zabel u32 nr_resets; 12081c22ad0SPhilipp Zabel bool active_low; 121adf20d7cSPhilipp Zabel bool status_active_low; 122adf20d7cSPhilipp Zabel }; 123adf20d7cSPhilipp Zabel 124adf20d7cSPhilipp Zabel #define SOCFPGA_NR_BANKS 8 125adf20d7cSPhilipp Zabel 126adf20d7cSPhilipp Zabel static const struct reset_simple_devdata reset_simple_socfpga = { 127b3ca9888SDinh Nguyen .reg_offset = 0x20, 128adf20d7cSPhilipp Zabel .nr_resets = SOCFPGA_NR_BANKS * 32, 129adf20d7cSPhilipp Zabel .status_active_low = true, 13081c22ad0SPhilipp Zabel }; 13181c22ad0SPhilipp Zabel 13281c22ad0SPhilipp Zabel static const struct reset_simple_devdata reset_simple_active_low = { 13381c22ad0SPhilipp Zabel .active_low = true, 134adf20d7cSPhilipp Zabel .status_active_low = true, 13581c22ad0SPhilipp Zabel }; 13681c22ad0SPhilipp Zabel 13781c22ad0SPhilipp Zabel static const struct of_device_id reset_simple_dt_ids[] = { 138b3ca9888SDinh Nguyen { .compatible = "altr,stratix10-rst-mgr", 139b3ca9888SDinh Nguyen .data = &reset_simple_socfpga }, 1400af8a137SPhilipp Zabel { .compatible = "st,stm32-rcc", }, 14181c22ad0SPhilipp Zabel { .compatible = "allwinner,sun6i-a31-clock-reset", 14281c22ad0SPhilipp Zabel .data = &reset_simple_active_low }, 143f0e0ada6SPhilipp Zabel { .compatible = "zte,zx296718-reset", 144f0e0ada6SPhilipp Zabel .data = &reset_simple_active_low }, 1451d7592f8SJoel Stanley { .compatible = "aspeed,ast2400-lpc-reset" }, 1461d7592f8SJoel Stanley { .compatible = "aspeed,ast2500-lpc-reset" }, 14764c47b62SManivannan Sadhasivam { .compatible = "bitmain,bm1880-reset", 14864c47b62SManivannan Sadhasivam .data = &reset_simple_active_low }, 149ea651ffdSGustavo Pimentel { .compatible = "snps,dw-high-reset" }, 150ea651ffdSGustavo Pimentel { .compatible = "snps,dw-low-reset", 151ea651ffdSGustavo Pimentel .data = &reset_simple_active_low }, 15281c22ad0SPhilipp Zabel { /* sentinel */ }, 15381c22ad0SPhilipp Zabel }; 15481c22ad0SPhilipp Zabel 15581c22ad0SPhilipp Zabel static int reset_simple_probe(struct platform_device *pdev) 15681c22ad0SPhilipp Zabel { 15781c22ad0SPhilipp Zabel struct device *dev = &pdev->dev; 15881c22ad0SPhilipp Zabel const struct reset_simple_devdata *devdata; 15981c22ad0SPhilipp Zabel struct reset_simple_data *data; 16081c22ad0SPhilipp Zabel void __iomem *membase; 16181c22ad0SPhilipp Zabel struct resource *res; 162adf20d7cSPhilipp Zabel u32 reg_offset = 0; 16381c22ad0SPhilipp Zabel 16481c22ad0SPhilipp Zabel devdata = of_device_get_match_data(dev); 16581c22ad0SPhilipp Zabel 16681c22ad0SPhilipp Zabel data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 16781c22ad0SPhilipp Zabel if (!data) 16881c22ad0SPhilipp Zabel return -ENOMEM; 16981c22ad0SPhilipp Zabel 17081c22ad0SPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 17181c22ad0SPhilipp Zabel membase = devm_ioremap_resource(dev, res); 17281c22ad0SPhilipp Zabel if (IS_ERR(membase)) 17381c22ad0SPhilipp Zabel return PTR_ERR(membase); 17481c22ad0SPhilipp Zabel 17581c22ad0SPhilipp Zabel spin_lock_init(&data->lock); 17681c22ad0SPhilipp Zabel data->membase = membase; 17781c22ad0SPhilipp Zabel data->rcdev.owner = THIS_MODULE; 17881c22ad0SPhilipp Zabel data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE; 17981c22ad0SPhilipp Zabel data->rcdev.ops = &reset_simple_ops; 18081c22ad0SPhilipp Zabel data->rcdev.of_node = dev->of_node; 18181c22ad0SPhilipp Zabel 182adf20d7cSPhilipp Zabel if (devdata) { 183adf20d7cSPhilipp Zabel reg_offset = devdata->reg_offset; 184adf20d7cSPhilipp Zabel if (devdata->nr_resets) 185adf20d7cSPhilipp Zabel data->rcdev.nr_resets = devdata->nr_resets; 18681c22ad0SPhilipp Zabel data->active_low = devdata->active_low; 187adf20d7cSPhilipp Zabel data->status_active_low = devdata->status_active_low; 188adf20d7cSPhilipp Zabel } 189adf20d7cSPhilipp Zabel 190adf20d7cSPhilipp Zabel data->membase += reg_offset; 19181c22ad0SPhilipp Zabel 19281c22ad0SPhilipp Zabel return devm_reset_controller_register(dev, &data->rcdev); 19381c22ad0SPhilipp Zabel } 19481c22ad0SPhilipp Zabel 19581c22ad0SPhilipp Zabel static struct platform_driver reset_simple_driver = { 19681c22ad0SPhilipp Zabel .probe = reset_simple_probe, 19781c22ad0SPhilipp Zabel .driver = { 19881c22ad0SPhilipp Zabel .name = "simple-reset", 19981c22ad0SPhilipp Zabel .of_match_table = reset_simple_dt_ids, 20081c22ad0SPhilipp Zabel }, 20181c22ad0SPhilipp Zabel }; 20281c22ad0SPhilipp Zabel builtin_platform_driver(reset_simple_driver); 203