162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hisilicon Hi6220 reset controller driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2016 Linaro Limited. 662306a36Sopenharmony_ci * Copyright (c) 2015-2016 HiSilicon Limited. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Feng Chen <puck.chen@hisilicon.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1862306a36Sopenharmony_ci#include <linux/reset-controller.h> 1962306a36Sopenharmony_ci#include <linux/reset.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PERIPH_ASSERT_OFFSET 0x300 2362306a36Sopenharmony_ci#define PERIPH_DEASSERT_OFFSET 0x304 2462306a36Sopenharmony_ci#define PERIPH_MAX_INDEX 0x509 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SC_MEDIA_RSTEN 0x052C 2762306a36Sopenharmony_ci#define SC_MEDIA_RSTDIS 0x0530 2862306a36Sopenharmony_ci#define MEDIA_MAX_INDEX 8 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum hi6220_reset_ctrl_type { 3362306a36Sopenharmony_ci PERIPHERAL, 3462306a36Sopenharmony_ci MEDIA, 3562306a36Sopenharmony_ci AO, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct hi6220_reset_data { 3962306a36Sopenharmony_ci struct reset_controller_dev rc_dev; 4062306a36Sopenharmony_ci struct regmap *regmap; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev, 4462306a36Sopenharmony_ci unsigned long idx) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 4762306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 4862306a36Sopenharmony_ci u32 bank = idx >> 8; 4962306a36Sopenharmony_ci u32 offset = idx & 0xff; 5062306a36Sopenharmony_ci u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return regmap_write(regmap, reg, BIT(offset)); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev, 5662306a36Sopenharmony_ci unsigned long idx) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 5962306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 6062306a36Sopenharmony_ci u32 bank = idx >> 8; 6162306a36Sopenharmony_ci u32 offset = idx & 0xff; 6262306a36Sopenharmony_ci u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return regmap_write(regmap, reg, BIT(offset)); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic const struct reset_control_ops hi6220_peripheral_reset_ops = { 6862306a36Sopenharmony_ci .assert = hi6220_peripheral_assert, 6962306a36Sopenharmony_ci .deassert = hi6220_peripheral_deassert, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int hi6220_media_assert(struct reset_controller_dev *rc_dev, 7362306a36Sopenharmony_ci unsigned long idx) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 7662306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx)); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int hi6220_media_deassert(struct reset_controller_dev *rc_dev, 8262306a36Sopenharmony_ci unsigned long idx) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 8562306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx)); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const struct reset_control_ops hi6220_media_reset_ops = { 9162306a36Sopenharmony_ci .assert = hi6220_media_assert, 9262306a36Sopenharmony_ci .deassert = hi6220_media_deassert, 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_CLKEN0 0x800 9662306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_CLKDIS0 0x804 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_RSTEN0 0x810 9962306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_RSTDIS0 0x814 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_ISOEN0 0x820 10262306a36Sopenharmony_ci#define AO_SCTRL_SC_PW_ISODIS0 0x824 10362306a36Sopenharmony_ci#define AO_MAX_INDEX 12 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int hi6220_ao_assert(struct reset_controller_dev *rc_dev, 10662306a36Sopenharmony_ci unsigned long idx) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 10962306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTEN0, BIT(idx)); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISOEN0, BIT(idx)); 11762306a36Sopenharmony_ci if (ret) 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKDIS0, BIT(idx)); 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int hi6220_ao_deassert(struct reset_controller_dev *rc_dev, 12562306a36Sopenharmony_ci unsigned long idx) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct hi6220_reset_data *data = to_reset_data(rc_dev); 12862306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * It was suggested to disable isolation before enabling 13362306a36Sopenharmony_ci * the clocks and deasserting reset, to avoid glitches. 13462306a36Sopenharmony_ci * But this order is preserved to keep it matching the 13562306a36Sopenharmony_ci * vendor code. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTDIS0, BIT(idx)); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISODIS0, BIT(idx)); 14262306a36Sopenharmony_ci if (ret) 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKEN0, BIT(idx)); 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const struct reset_control_ops hi6220_ao_reset_ops = { 15062306a36Sopenharmony_ci .assert = hi6220_ao_assert, 15162306a36Sopenharmony_ci .deassert = hi6220_ao_deassert, 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int hi6220_reset_probe(struct platform_device *pdev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 15762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 15862306a36Sopenharmony_ci enum hi6220_reset_ctrl_type type; 15962306a36Sopenharmony_ci struct hi6220_reset_data *data; 16062306a36Sopenharmony_ci struct regmap *regmap; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 16362306a36Sopenharmony_ci if (!data) 16462306a36Sopenharmony_ci return -ENOMEM; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci type = (uintptr_t)of_device_get_match_data(dev); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci regmap = syscon_node_to_regmap(np); 16962306a36Sopenharmony_ci if (IS_ERR(regmap)) { 17062306a36Sopenharmony_ci dev_err(dev, "failed to get reset controller regmap\n"); 17162306a36Sopenharmony_ci return PTR_ERR(regmap); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci data->regmap = regmap; 17562306a36Sopenharmony_ci data->rc_dev.of_node = np; 17662306a36Sopenharmony_ci if (type == MEDIA) { 17762306a36Sopenharmony_ci data->rc_dev.ops = &hi6220_media_reset_ops; 17862306a36Sopenharmony_ci data->rc_dev.nr_resets = MEDIA_MAX_INDEX; 17962306a36Sopenharmony_ci } else if (type == PERIPHERAL) { 18062306a36Sopenharmony_ci data->rc_dev.ops = &hi6220_peripheral_reset_ops; 18162306a36Sopenharmony_ci data->rc_dev.nr_resets = PERIPH_MAX_INDEX; 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci data->rc_dev.ops = &hi6220_ao_reset_ops; 18462306a36Sopenharmony_ci data->rc_dev.nr_resets = AO_MAX_INDEX; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return reset_controller_register(&data->rc_dev); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct of_device_id hi6220_reset_match[] = { 19162306a36Sopenharmony_ci { 19262306a36Sopenharmony_ci .compatible = "hisilicon,hi6220-sysctrl", 19362306a36Sopenharmony_ci .data = (void *)PERIPHERAL, 19462306a36Sopenharmony_ci }, 19562306a36Sopenharmony_ci { 19662306a36Sopenharmony_ci .compatible = "hisilicon,hi6220-mediactrl", 19762306a36Sopenharmony_ci .data = (void *)MEDIA, 19862306a36Sopenharmony_ci }, 19962306a36Sopenharmony_ci { 20062306a36Sopenharmony_ci .compatible = "hisilicon,hi6220-aoctrl", 20162306a36Sopenharmony_ci .data = (void *)AO, 20262306a36Sopenharmony_ci }, 20362306a36Sopenharmony_ci { /* sentinel */ }, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hi6220_reset_match); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic struct platform_driver hi6220_reset_driver = { 20862306a36Sopenharmony_ci .probe = hi6220_reset_probe, 20962306a36Sopenharmony_ci .driver = { 21062306a36Sopenharmony_ci .name = "reset-hi6220", 21162306a36Sopenharmony_ci .of_match_table = hi6220_reset_match, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int __init hi6220_reset_init(void) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci return platform_driver_register(&hi6220_reset_driver); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cipostcore_initcall(hi6220_reset_init); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 223