1/* 2 * Copyright (C) 2015 Alban Bedel <albeu@free.fr> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15#include <linux/module.h> 16#include <linux/platform_device.h> 17#include <linux/reset-controller.h> 18 19struct ath79_reset { 20 struct reset_controller_dev rcdev; 21 void __iomem *base; 22 spinlock_t lock; 23}; 24 25static int ath79_reset_update(struct reset_controller_dev *rcdev, 26 unsigned long id, bool assert) 27{ 28 struct ath79_reset *ath79_reset = 29 container_of(rcdev, struct ath79_reset, rcdev); 30 unsigned long flags; 31 u32 val; 32 33 spin_lock_irqsave(&ath79_reset->lock, flags); 34 val = readl(ath79_reset->base); 35 if (assert) 36 val |= BIT(id); 37 else 38 val &= ~BIT(id); 39 writel(val, ath79_reset->base); 40 spin_unlock_irqrestore(&ath79_reset->lock, flags); 41 42 return 0; 43} 44 45static int ath79_reset_assert(struct reset_controller_dev *rcdev, 46 unsigned long id) 47{ 48 return ath79_reset_update(rcdev, id, true); 49} 50 51static int ath79_reset_deassert(struct reset_controller_dev *rcdev, 52 unsigned long id) 53{ 54 return ath79_reset_update(rcdev, id, false); 55} 56 57static int ath79_reset_status(struct reset_controller_dev *rcdev, 58 unsigned long id) 59{ 60 struct ath79_reset *ath79_reset = 61 container_of(rcdev, struct ath79_reset, rcdev); 62 u32 val; 63 64 val = readl(ath79_reset->base); 65 66 return !!(val & BIT(id)); 67} 68 69static struct reset_control_ops ath79_reset_ops = { 70 .assert = ath79_reset_assert, 71 .deassert = ath79_reset_deassert, 72 .status = ath79_reset_status, 73}; 74 75static int ath79_reset_probe(struct platform_device *pdev) 76{ 77 struct ath79_reset *ath79_reset; 78 struct resource *res; 79 80 ath79_reset = devm_kzalloc(&pdev->dev, 81 sizeof(*ath79_reset), GFP_KERNEL); 82 if (!ath79_reset) 83 return -ENOMEM; 84 85 platform_set_drvdata(pdev, ath79_reset); 86 87 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 88 ath79_reset->base = devm_ioremap_resource(&pdev->dev, res); 89 if (IS_ERR(ath79_reset->base)) 90 return PTR_ERR(ath79_reset->base); 91 92 spin_lock_init(&ath79_reset->lock); 93 ath79_reset->rcdev.ops = &ath79_reset_ops; 94 ath79_reset->rcdev.owner = THIS_MODULE; 95 ath79_reset->rcdev.of_node = pdev->dev.of_node; 96 ath79_reset->rcdev.of_reset_n_cells = 1; 97 ath79_reset->rcdev.nr_resets = 32; 98 99 return reset_controller_register(&ath79_reset->rcdev); 100} 101 102static int ath79_reset_remove(struct platform_device *pdev) 103{ 104 struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); 105 106 reset_controller_unregister(&ath79_reset->rcdev); 107 108 return 0; 109} 110 111static const struct of_device_id ath79_reset_dt_ids[] = { 112 { .compatible = "qca,ar7100-reset", }, 113 { }, 114}; 115MODULE_DEVICE_TABLE(of, ath79_reset_dt_ids); 116 117static struct platform_driver ath79_reset_driver = { 118 .probe = ath79_reset_probe, 119 .remove = ath79_reset_remove, 120 .driver = { 121 .name = "ath79-reset", 122 .of_match_table = ath79_reset_dt_ids, 123 }, 124}; 125module_platform_driver(ath79_reset_driver); 126 127MODULE_AUTHOR("Alban Bedel <albeu@free.fr>"); 128MODULE_DESCRIPTION("AR71xx Reset Controller Driver"); 129MODULE_LICENSE("GPL"); 130