18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Intel Corporation. 48c2ecf20Sopenharmony_ci * Lei Chuanhua <Chuanhua.lei@intel.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/of_device.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/reboot.h> 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci#include <linux/reset-controller.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define RCU_RST_STAT 0x0024 168c2ecf20Sopenharmony_ci#define RCU_RST_REQ 0x0048 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define REG_OFFSET_MASK GENMASK(31, 16) 198c2ecf20Sopenharmony_ci#define BIT_OFFSET_MASK GENMASK(15, 8) 208c2ecf20Sopenharmony_ci#define STAT_BIT_OFFSET_MASK GENMASK(7, 0) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define to_reset_data(x) container_of(x, struct intel_reset_data, rcdev) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct intel_reset_soc { 258c2ecf20Sopenharmony_ci bool legacy; 268c2ecf20Sopenharmony_ci u32 reset_cell_count; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct intel_reset_data { 308c2ecf20Sopenharmony_ci struct reset_controller_dev rcdev; 318c2ecf20Sopenharmony_ci struct notifier_block restart_nb; 328c2ecf20Sopenharmony_ci const struct intel_reset_soc *soc_data; 338c2ecf20Sopenharmony_ci struct regmap *regmap; 348c2ecf20Sopenharmony_ci struct device *dev; 358c2ecf20Sopenharmony_ci u32 reboot_id; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const struct regmap_config intel_rcu_regmap_config = { 398c2ecf20Sopenharmony_ci .name = "intel-reset", 408c2ecf20Sopenharmony_ci .reg_bits = 32, 418c2ecf20Sopenharmony_ci .reg_stride = 4, 428c2ecf20Sopenharmony_ci .val_bits = 32, 438c2ecf20Sopenharmony_ci .fast_io = true, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Reset status register offset relative to 488c2ecf20Sopenharmony_ci * the reset control register(X) is X + 4 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic u32 id_to_reg_and_bit_offsets(struct intel_reset_data *data, 518c2ecf20Sopenharmony_ci unsigned long id, u32 *rst_req, 528c2ecf20Sopenharmony_ci u32 *req_bit, u32 *stat_bit) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci *rst_req = FIELD_GET(REG_OFFSET_MASK, id); 558c2ecf20Sopenharmony_ci *req_bit = FIELD_GET(BIT_OFFSET_MASK, id); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (data->soc_data->legacy) 588c2ecf20Sopenharmony_ci *stat_bit = FIELD_GET(STAT_BIT_OFFSET_MASK, id); 598c2ecf20Sopenharmony_ci else 608c2ecf20Sopenharmony_ci *stat_bit = *req_bit; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (data->soc_data->legacy && *rst_req == RCU_RST_REQ) 638c2ecf20Sopenharmony_ci return RCU_RST_STAT; 648c2ecf20Sopenharmony_ci else 658c2ecf20Sopenharmony_ci return *rst_req + 0x4; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int intel_set_clr_bits(struct intel_reset_data *data, unsigned long id, 698c2ecf20Sopenharmony_ci bool set) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 rst_req, req_bit, rst_stat, stat_bit, val; 728c2ecf20Sopenharmony_ci int ret; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci rst_stat = id_to_reg_and_bit_offsets(data, id, &rst_req, 758c2ecf20Sopenharmony_ci &req_bit, &stat_bit); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci val = set ? BIT(req_bit) : 0; 788c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, rst_req, BIT(req_bit), val); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return regmap_read_poll_timeout(data->regmap, rst_stat, val, 838c2ecf20Sopenharmony_ci set == !!(val & BIT(stat_bit)), 20, 848c2ecf20Sopenharmony_ci 200); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int intel_assert_device(struct reset_controller_dev *rcdev, 888c2ecf20Sopenharmony_ci unsigned long id) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct intel_reset_data *data = to_reset_data(rcdev); 918c2ecf20Sopenharmony_ci int ret; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = intel_set_clr_bits(data, id, true); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci dev_err(data->dev, "Reset assert failed %d\n", ret); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int intel_deassert_device(struct reset_controller_dev *rcdev, 1018c2ecf20Sopenharmony_ci unsigned long id) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct intel_reset_data *data = to_reset_data(rcdev); 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = intel_set_clr_bits(data, id, false); 1078c2ecf20Sopenharmony_ci if (ret) 1088c2ecf20Sopenharmony_ci dev_err(data->dev, "Reset deassert failed %d\n", ret); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int intel_reset_status(struct reset_controller_dev *rcdev, 1148c2ecf20Sopenharmony_ci unsigned long id) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct intel_reset_data *data = to_reset_data(rcdev); 1178c2ecf20Sopenharmony_ci u32 rst_req, req_bit, rst_stat, stat_bit, val; 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rst_stat = id_to_reg_and_bit_offsets(data, id, &rst_req, 1218c2ecf20Sopenharmony_ci &req_bit, &stat_bit); 1228c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, rst_stat, &val); 1238c2ecf20Sopenharmony_ci if (ret) 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return !!(val & BIT(stat_bit)); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct reset_control_ops intel_reset_ops = { 1308c2ecf20Sopenharmony_ci .assert = intel_assert_device, 1318c2ecf20Sopenharmony_ci .deassert = intel_deassert_device, 1328c2ecf20Sopenharmony_ci .status = intel_reset_status, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int intel_reset_xlate(struct reset_controller_dev *rcdev, 1368c2ecf20Sopenharmony_ci const struct of_phandle_args *spec) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct intel_reset_data *data = to_reset_data(rcdev); 1398c2ecf20Sopenharmony_ci u32 id; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (spec->args[1] > 31) 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci id = FIELD_PREP(REG_OFFSET_MASK, spec->args[0]); 1458c2ecf20Sopenharmony_ci id |= FIELD_PREP(BIT_OFFSET_MASK, spec->args[1]); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (data->soc_data->legacy) { 1488c2ecf20Sopenharmony_ci if (spec->args[2] > 31) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, spec->args[2]); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return id; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int intel_reset_restart_handler(struct notifier_block *nb, 1588c2ecf20Sopenharmony_ci unsigned long action, void *data) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct intel_reset_data *reset_data; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci reset_data = container_of(nb, struct intel_reset_data, restart_nb); 1638c2ecf20Sopenharmony_ci intel_assert_device(&reset_data->rcdev, reset_data->reboot_id); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int intel_reset_probe(struct platform_device *pdev) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 1718c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1728c2ecf20Sopenharmony_ci struct intel_reset_data *data; 1738c2ecf20Sopenharmony_ci void __iomem *base; 1748c2ecf20Sopenharmony_ci u32 rb_id[3]; 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 1788c2ecf20Sopenharmony_ci if (!data) 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci data->soc_data = of_device_get_match_data(dev); 1828c2ecf20Sopenharmony_ci if (!data->soc_data) 1838c2ecf20Sopenharmony_ci return -ENODEV; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 1868c2ecf20Sopenharmony_ci if (IS_ERR(base)) 1878c2ecf20Sopenharmony_ci return PTR_ERR(base); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci data->regmap = devm_regmap_init_mmio(dev, base, 1908c2ecf20Sopenharmony_ci &intel_rcu_regmap_config); 1918c2ecf20Sopenharmony_ci if (IS_ERR(data->regmap)) { 1928c2ecf20Sopenharmony_ci dev_err(dev, "regmap initialization failed\n"); 1938c2ecf20Sopenharmony_ci return PTR_ERR(data->regmap); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = device_property_read_u32_array(dev, "intel,global-reset", rb_id, 1978c2ecf20Sopenharmony_ci data->soc_data->reset_cell_count); 1988c2ecf20Sopenharmony_ci if (ret) { 1998c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get global reset offset!\n"); 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci data->dev = dev; 2048c2ecf20Sopenharmony_ci data->rcdev.of_node = np; 2058c2ecf20Sopenharmony_ci data->rcdev.owner = dev->driver->owner; 2068c2ecf20Sopenharmony_ci data->rcdev.ops = &intel_reset_ops; 2078c2ecf20Sopenharmony_ci data->rcdev.of_xlate = intel_reset_xlate; 2088c2ecf20Sopenharmony_ci data->rcdev.of_reset_n_cells = data->soc_data->reset_cell_count; 2098c2ecf20Sopenharmony_ci ret = devm_reset_controller_register(&pdev->dev, &data->rcdev); 2108c2ecf20Sopenharmony_ci if (ret) 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci data->reboot_id = FIELD_PREP(REG_OFFSET_MASK, rb_id[0]); 2148c2ecf20Sopenharmony_ci data->reboot_id |= FIELD_PREP(BIT_OFFSET_MASK, rb_id[1]); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (data->soc_data->legacy) 2178c2ecf20Sopenharmony_ci data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci data->restart_nb.notifier_call = intel_reset_restart_handler; 2208c2ecf20Sopenharmony_ci data->restart_nb.priority = 128; 2218c2ecf20Sopenharmony_ci register_restart_handler(&data->restart_nb); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct intel_reset_soc xrx200_data = { 2278c2ecf20Sopenharmony_ci .legacy = true, 2288c2ecf20Sopenharmony_ci .reset_cell_count = 3, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic const struct intel_reset_soc lgm_data = { 2328c2ecf20Sopenharmony_ci .legacy = false, 2338c2ecf20Sopenharmony_ci .reset_cell_count = 2, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic const struct of_device_id intel_reset_match[] = { 2378c2ecf20Sopenharmony_ci { .compatible = "intel,rcu-lgm", .data = &lgm_data }, 2388c2ecf20Sopenharmony_ci { .compatible = "intel,rcu-xrx200", .data = &xrx200_data }, 2398c2ecf20Sopenharmony_ci {} 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct platform_driver intel_reset_driver = { 2438c2ecf20Sopenharmony_ci .probe = intel_reset_probe, 2448c2ecf20Sopenharmony_ci .driver = { 2458c2ecf20Sopenharmony_ci .name = "intel-reset", 2468c2ecf20Sopenharmony_ci .of_match_table = intel_reset_match, 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int __init intel_reset_init(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci return platform_driver_register(&intel_reset_driver); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * RCU is system core entity which is in Always On Domain whose clocks 2578c2ecf20Sopenharmony_ci * or resource initialization happens in system core initialization. 2588c2ecf20Sopenharmony_ci * Also, it is required for most of the platform or architecture 2598c2ecf20Sopenharmony_ci * specific devices to perform reset operation as part of initialization. 2608c2ecf20Sopenharmony_ci * So perform RCU as post core initialization. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cipostcore_initcall(intel_reset_init); 263