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 19 struct ath79_reset { 20 struct reset_controller_dev rcdev; 21 void __iomem *base; 22 spinlock_t lock; 23 }; 24 25 static 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 45 static int ath79_reset_assert(struct reset_controller_dev *rcdev, 46 unsigned long id) 47 { 48 return ath79_reset_update(rcdev, id, true); 49 } 50 51 static int ath79_reset_deassert(struct reset_controller_dev *rcdev, 52 unsigned long id) 53 { 54 return ath79_reset_update(rcdev, id, false); 55 } 56 57 static 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 69 static struct reset_control_ops ath79_reset_ops = { 70 .assert = ath79_reset_assert, 71 .deassert = ath79_reset_deassert, 72 .status = ath79_reset_status, 73 }; 74 75 static 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 ath79_reset->rcdev.ops = &ath79_reset_ops; 93 ath79_reset->rcdev.owner = THIS_MODULE; 94 ath79_reset->rcdev.of_node = pdev->dev.of_node; 95 ath79_reset->rcdev.of_reset_n_cells = 1; 96 ath79_reset->rcdev.nr_resets = 32; 97 98 return reset_controller_register(&ath79_reset->rcdev); 99 } 100 101 static int ath79_reset_remove(struct platform_device *pdev) 102 { 103 struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); 104 105 reset_controller_unregister(&ath79_reset->rcdev); 106 107 return 0; 108 } 109 110 static const struct of_device_id ath79_reset_dt_ids[] = { 111 { .compatible = "qca,ar7100-reset", }, 112 { }, 113 }; 114 MODULE_DEVICE_TABLE(of, ath79_reset_dt_ids); 115 116 static struct platform_driver ath79_reset_driver = { 117 .probe = ath79_reset_probe, 118 .remove = ath79_reset_remove, 119 .driver = { 120 .name = "ath79-reset", 121 .of_match_table = ath79_reset_dt_ids, 122 }, 123 }; 124 module_platform_driver(ath79_reset_driver); 125 126 MODULE_AUTHOR("Alban Bedel <albeu@free.fr>"); 127 MODULE_DESCRIPTION("AR71xx Reset Controller Driver"); 128 MODULE_LICENSE("GPL"); 129