1 /* 2 * Pistachio SoC Reset Controller driver 3 * 4 * Copyright (C) 2015 Imagination Technologies Ltd. 5 * 6 * Author: Damien Horsley <Damien.Horsley@imgtec.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2, as published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/regmap.h> 17 #include <linux/reset-controller.h> 18 #include <linux/slab.h> 19 #include <linux/mfd/syscon.h> 20 21 #include <dt-bindings/reset/pistachio-resets.h> 22 23 #define PISTACHIO_SOFT_RESET 0 24 25 struct pistachio_reset_data { 26 struct reset_controller_dev rcdev; 27 struct regmap *periph_regs; 28 }; 29 30 static inline int pistachio_reset_shift(unsigned long id) 31 { 32 switch (id) { 33 case PISTACHIO_RESET_I2C0: 34 case PISTACHIO_RESET_I2C1: 35 case PISTACHIO_RESET_I2C2: 36 case PISTACHIO_RESET_I2C3: 37 case PISTACHIO_RESET_I2S_IN: 38 case PISTACHIO_RESET_PRL_OUT: 39 case PISTACHIO_RESET_SPDIF_OUT: 40 case PISTACHIO_RESET_SPI: 41 case PISTACHIO_RESET_PWM_PDM: 42 case PISTACHIO_RESET_UART0: 43 case PISTACHIO_RESET_UART1: 44 case PISTACHIO_RESET_QSPI: 45 case PISTACHIO_RESET_MDC: 46 case PISTACHIO_RESET_SDHOST: 47 case PISTACHIO_RESET_ETHERNET: 48 case PISTACHIO_RESET_IR: 49 case PISTACHIO_RESET_HASH: 50 case PISTACHIO_RESET_TIMER: 51 return id; 52 case PISTACHIO_RESET_I2S_OUT: 53 case PISTACHIO_RESET_SPDIF_IN: 54 case PISTACHIO_RESET_EVT: 55 return id + 6; 56 case PISTACHIO_RESET_USB_H: 57 case PISTACHIO_RESET_USB_PR: 58 case PISTACHIO_RESET_USB_PHY_PR: 59 case PISTACHIO_RESET_USB_PHY_PON: 60 return id + 7; 61 default: 62 return -EINVAL; 63 } 64 } 65 66 static int pistachio_reset_assert(struct reset_controller_dev *rcdev, 67 unsigned long id) 68 { 69 struct pistachio_reset_data *rd; 70 u32 mask; 71 int shift; 72 73 rd = container_of(rcdev, struct pistachio_reset_data, rcdev); 74 shift = pistachio_reset_shift(id); 75 if (shift < 0) 76 return shift; 77 mask = BIT(shift); 78 79 return regmap_update_bits(rd->periph_regs, PISTACHIO_SOFT_RESET, 80 mask, mask); 81 } 82 83 static int pistachio_reset_deassert(struct reset_controller_dev *rcdev, 84 unsigned long id) 85 { 86 struct pistachio_reset_data *rd; 87 u32 mask; 88 int shift; 89 90 rd = container_of(rcdev, struct pistachio_reset_data, rcdev); 91 shift = pistachio_reset_shift(id); 92 if (shift < 0) 93 return shift; 94 mask = BIT(shift); 95 96 return regmap_update_bits(rd->periph_regs, PISTACHIO_SOFT_RESET, 97 mask, 0); 98 } 99 100 static const struct reset_control_ops pistachio_reset_ops = { 101 .assert = pistachio_reset_assert, 102 .deassert = pistachio_reset_deassert, 103 }; 104 105 static int pistachio_reset_probe(struct platform_device *pdev) 106 { 107 struct pistachio_reset_data *rd; 108 struct device *dev = &pdev->dev; 109 struct device_node *np = pdev->dev.of_node; 110 111 rd = devm_kzalloc(dev, sizeof(*rd), GFP_KERNEL); 112 if (!rd) 113 return -ENOMEM; 114 115 rd->periph_regs = syscon_node_to_regmap(np->parent); 116 if (IS_ERR(rd->periph_regs)) 117 return PTR_ERR(rd->periph_regs); 118 119 rd->rcdev.owner = THIS_MODULE; 120 rd->rcdev.nr_resets = PISTACHIO_RESET_MAX + 1; 121 rd->rcdev.ops = &pistachio_reset_ops; 122 rd->rcdev.of_node = np; 123 124 return devm_reset_controller_register(dev, &rd->rcdev); 125 } 126 127 static const struct of_device_id pistachio_reset_dt_ids[] = { 128 { .compatible = "img,pistachio-reset", }, 129 { /* sentinel */ }, 130 }; 131 MODULE_DEVICE_TABLE(of, pistachio_reset_dt_ids); 132 133 static struct platform_driver pistachio_reset_driver = { 134 .probe = pistachio_reset_probe, 135 .driver = { 136 .name = "pistachio-reset", 137 .of_match_table = pistachio_reset_dt_ids, 138 }, 139 }; 140 module_platform_driver(pistachio_reset_driver); 141 142 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 143 MODULE_DESCRIPTION("Pistacho Reset Controller Driver"); 144 MODULE_LICENSE("GPL v2"); 145