162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AppliedMicro X-Gene SoC Reboot Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013, Applied Micro Circuits Corporation 662306a36Sopenharmony_ci * Author: Feng Kan <fkan@apm.com> 762306a36Sopenharmony_ci * Author: Loc Ho <lho@apm.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This driver provides system reboot functionality for APM X-Gene SoC. 1062306a36Sopenharmony_ci * For system shutdown, this is board specify. If a board designer 1162306a36Sopenharmony_ci * implements GPIO shutdown, use the gpio-poweroff.c driver. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/notifier.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/reboot.h> 2062306a36Sopenharmony_ci#include <linux/stat.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct xgene_reboot_context { 2462306a36Sopenharmony_ci struct device *dev; 2562306a36Sopenharmony_ci void *csr; 2662306a36Sopenharmony_ci u32 mask; 2762306a36Sopenharmony_ci struct notifier_block restart_handler; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int xgene_restart_handler(struct notifier_block *this, 3162306a36Sopenharmony_ci unsigned long mode, void *cmd) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct xgene_reboot_context *ctx = 3462306a36Sopenharmony_ci container_of(this, struct xgene_reboot_context, 3562306a36Sopenharmony_ci restart_handler); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Issue the reboot */ 3862306a36Sopenharmony_ci writel(ctx->mask, ctx->csr); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci mdelay(1000); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci dev_emerg(ctx->dev, "Unable to restart system\n"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return NOTIFY_DONE; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int xgene_reboot_probe(struct platform_device *pdev) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct xgene_reboot_context *ctx; 5062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 5162306a36Sopenharmony_ci int err; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 5462306a36Sopenharmony_ci if (!ctx) 5562306a36Sopenharmony_ci return -ENOMEM; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ctx->csr = of_iomap(dev->of_node, 0); 5862306a36Sopenharmony_ci if (!ctx->csr) { 5962306a36Sopenharmony_ci dev_err(dev, "can not map resource\n"); 6062306a36Sopenharmony_ci return -ENODEV; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "mask", &ctx->mask)) 6462306a36Sopenharmony_ci ctx->mask = 0xFFFFFFFF; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ctx->dev = dev; 6762306a36Sopenharmony_ci ctx->restart_handler.notifier_call = xgene_restart_handler; 6862306a36Sopenharmony_ci ctx->restart_handler.priority = 128; 6962306a36Sopenharmony_ci err = register_restart_handler(&ctx->restart_handler); 7062306a36Sopenharmony_ci if (err) { 7162306a36Sopenharmony_ci iounmap(ctx->csr); 7262306a36Sopenharmony_ci dev_err(dev, "cannot register restart handler (err=%d)\n", err); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return err; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct of_device_id xgene_reboot_of_match[] = { 7962306a36Sopenharmony_ci { .compatible = "apm,xgene-reboot" }, 8062306a36Sopenharmony_ci {} 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct platform_driver xgene_reboot_driver = { 8462306a36Sopenharmony_ci .probe = xgene_reboot_probe, 8562306a36Sopenharmony_ci .driver = { 8662306a36Sopenharmony_ci .name = "xgene-reboot", 8762306a36Sopenharmony_ci .of_match_table = xgene_reboot_of_match, 8862306a36Sopenharmony_ci }, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int __init xgene_reboot_init(void) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return platform_driver_register(&xgene_reboot_driver); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_cidevice_initcall(xgene_reboot_init); 96