162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BCM6345 Reset Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/reset-controller.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define BCM6345_RESET_NUM 32 1662306a36Sopenharmony_ci#define BCM6345_RESET_SLEEP_MIN_US 10000 1762306a36Sopenharmony_ci#define BCM6345_RESET_SLEEP_MAX_US 20000 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct bcm6345_reset { 2062306a36Sopenharmony_ci struct reset_controller_dev rcdev; 2162306a36Sopenharmony_ci void __iomem *base; 2262306a36Sopenharmony_ci spinlock_t lock; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic inline struct bcm6345_reset * 2662306a36Sopenharmony_cito_bcm6345_reset(struct reset_controller_dev *rcdev) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return container_of(rcdev, struct bcm6345_reset, rcdev); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int bcm6345_reset_update(struct reset_controller_dev *rcdev, 3262306a36Sopenharmony_ci unsigned long id, bool assert) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev); 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci uint32_t val; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci spin_lock_irqsave(&bcm6345_reset->lock, flags); 3962306a36Sopenharmony_ci val = __raw_readl(bcm6345_reset->base); 4062306a36Sopenharmony_ci if (assert) 4162306a36Sopenharmony_ci val &= ~BIT(id); 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci val |= BIT(id); 4462306a36Sopenharmony_ci __raw_writel(val, bcm6345_reset->base); 4562306a36Sopenharmony_ci spin_unlock_irqrestore(&bcm6345_reset->lock, flags); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int bcm6345_reset_assert(struct reset_controller_dev *rcdev, 5162306a36Sopenharmony_ci unsigned long id) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return bcm6345_reset_update(rcdev, id, true); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int bcm6345_reset_deassert(struct reset_controller_dev *rcdev, 5762306a36Sopenharmony_ci unsigned long id) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return bcm6345_reset_update(rcdev, id, false); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int bcm6345_reset_reset(struct reset_controller_dev *rcdev, 6362306a36Sopenharmony_ci unsigned long id) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci bcm6345_reset_update(rcdev, id, true); 6662306a36Sopenharmony_ci usleep_range(BCM6345_RESET_SLEEP_MIN_US, 6762306a36Sopenharmony_ci BCM6345_RESET_SLEEP_MAX_US); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci bcm6345_reset_update(rcdev, id, false); 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * Ensure component is taken out reset state by sleeping also after 7262306a36Sopenharmony_ci * deasserting the reset. Otherwise, the component may not be ready 7362306a36Sopenharmony_ci * for operation. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci usleep_range(BCM6345_RESET_SLEEP_MIN_US, 7662306a36Sopenharmony_ci BCM6345_RESET_SLEEP_MAX_US); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int bcm6345_reset_status(struct reset_controller_dev *rcdev, 8262306a36Sopenharmony_ci unsigned long id) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return !(__raw_readl(bcm6345_reset->base) & BIT(id)); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct reset_control_ops bcm6345_reset_ops = { 9062306a36Sopenharmony_ci .assert = bcm6345_reset_assert, 9162306a36Sopenharmony_ci .deassert = bcm6345_reset_deassert, 9262306a36Sopenharmony_ci .reset = bcm6345_reset_reset, 9362306a36Sopenharmony_ci .status = bcm6345_reset_status, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int bcm6345_reset_probe(struct platform_device *pdev) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct bcm6345_reset *bcm6345_reset; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci bcm6345_reset = devm_kzalloc(&pdev->dev, 10162306a36Sopenharmony_ci sizeof(*bcm6345_reset), GFP_KERNEL); 10262306a36Sopenharmony_ci if (!bcm6345_reset) 10362306a36Sopenharmony_ci return -ENOMEM; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci bcm6345_reset->base = devm_platform_ioremap_resource(pdev, 0); 10662306a36Sopenharmony_ci if (IS_ERR(bcm6345_reset->base)) 10762306a36Sopenharmony_ci return PTR_ERR(bcm6345_reset->base); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_lock_init(&bcm6345_reset->lock); 11062306a36Sopenharmony_ci bcm6345_reset->rcdev.ops = &bcm6345_reset_ops; 11162306a36Sopenharmony_ci bcm6345_reset->rcdev.owner = THIS_MODULE; 11262306a36Sopenharmony_ci bcm6345_reset->rcdev.of_node = pdev->dev.of_node; 11362306a36Sopenharmony_ci bcm6345_reset->rcdev.of_reset_n_cells = 1; 11462306a36Sopenharmony_ci bcm6345_reset->rcdev.nr_resets = BCM6345_RESET_NUM; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return devm_reset_controller_register(&pdev->dev, 11762306a36Sopenharmony_ci &bcm6345_reset->rcdev); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const struct of_device_id bcm6345_reset_of_match[] = { 12162306a36Sopenharmony_ci { .compatible = "brcm,bcm6345-reset" }, 12262306a36Sopenharmony_ci { /* sentinel */ }, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct platform_driver bcm6345_reset_driver = { 12662306a36Sopenharmony_ci .probe = bcm6345_reset_probe, 12762306a36Sopenharmony_ci .driver = { 12862306a36Sopenharmony_ci .name = "bcm6345-reset", 12962306a36Sopenharmony_ci .of_match_table = bcm6345_reset_of_match, 13062306a36Sopenharmony_ci .suppress_bind_attrs = true, 13162306a36Sopenharmony_ci }, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_cibuiltin_platform_driver(bcm6345_reset_driver); 134