1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * SAMA5D2 PIOBU GPIO controller 4 * 5 * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries 6 * 7 * Author: Andrei Stefanescu <andrei.stefanescu@microchip.com> 8 * 9 */ 10 #include <linux/bits.h> 11 #include <linux/gpio/driver.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/mfd/syscon.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/platform_device.h> 18 #include <linux/regmap.h> 19 20 #define PIOBU_NUM 8 21 #define PIOBU_REG_SIZE 4 22 23 /* 24 * backup mode protection register for tamper detection 25 * normal mode protection register for tamper detection 26 * wakeup signal generation 27 */ 28 #define PIOBU_BMPR 0x7C 29 #define PIOBU_NMPR 0x80 30 #define PIOBU_WKPR 0x90 31 32 #define PIOBU_BASE 0x18 /* PIOBU offset from SECUMOD base register address. */ 33 34 #define PIOBU_DET_OFFSET 16 35 36 /* In the datasheet this bit is called OUTPUT */ 37 #define PIOBU_DIRECTION BIT(8) 38 #define PIOBU_OUT BIT(8) 39 #define PIOBU_IN 0 40 41 #define PIOBU_SOD BIT(9) 42 #define PIOBU_PDS BIT(10) 43 44 #define PIOBU_HIGH BIT(9) 45 #define PIOBU_LOW 0 46 47 struct sama5d2_piobu { 48 struct gpio_chip chip; 49 struct regmap *regmap; 50 }; 51 52 /* 53 * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call 54 * 55 * Do not consider pin for tamper detection (normal and backup modes) 56 * Do not consider pin as tamper wakeup interrupt source 57 */ 58 static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin) 59 { 60 int ret; 61 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu, 62 chip); 63 unsigned int mask = BIT(PIOBU_DET_OFFSET + pin); 64 65 ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0); 66 if (ret) 67 return ret; 68 69 ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0); 70 if (ret) 71 return ret; 72 73 return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0); 74 } 75 76 /* 77 * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register 78 */ 79 static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, 80 unsigned int mask, unsigned int value) 81 { 82 int reg; 83 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu, 84 chip); 85 86 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE; 87 88 return regmap_update_bits(piobu->regmap, reg, mask, value); 89 } 90 91 /* 92 * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU 93 * register 94 */ 95 static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, 96 unsigned int mask) 97 { 98 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu, 99 chip); 100 unsigned int val, reg; 101 int ret; 102 103 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE; 104 ret = regmap_read(piobu->regmap, reg, &val); 105 if (ret < 0) 106 return ret; 107 108 return val & mask; 109 } 110 111 /* 112 * sama5d2_piobu_get_direction() - gpiochip get_direction 113 */ 114 static int sama5d2_piobu_get_direction(struct gpio_chip *chip, 115 unsigned int pin) 116 { 117 int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION); 118 119 if (ret < 0) 120 return ret; 121 122 return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN : 123 GPIO_LINE_DIRECTION_OUT; 124 } 125 126 /* 127 * sama5d2_piobu_direction_input() - gpiochip direction_input 128 */ 129 static int sama5d2_piobu_direction_input(struct gpio_chip *chip, 130 unsigned int pin) 131 { 132 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); 133 } 134 135 /* 136 * sama5d2_piobu_direction_output() - gpiochip direction_output 137 */ 138 static int sama5d2_piobu_direction_output(struct gpio_chip *chip, 139 unsigned int pin, int value) 140 { 141 unsigned int val = PIOBU_OUT; 142 143 if (value) 144 val |= PIOBU_HIGH; 145 146 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD, 147 val); 148 } 149 150 /* 151 * sama5d2_piobu_get() - gpiochip get 152 */ 153 static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) 154 { 155 /* if pin is input, read value from PDS else read from SOD */ 156 int ret = sama5d2_piobu_get_direction(chip, pin); 157 158 if (ret == GPIO_LINE_DIRECTION_IN) 159 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS); 160 else if (ret == GPIO_LINE_DIRECTION_OUT) 161 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD); 162 163 if (ret < 0) 164 return ret; 165 166 return !!ret; 167 } 168 169 /* 170 * sama5d2_piobu_set() - gpiochip set 171 */ 172 static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, 173 int value) 174 { 175 if (!value) 176 value = PIOBU_LOW; 177 else 178 value = PIOBU_HIGH; 179 180 sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); 181 } 182 183 static int sama5d2_piobu_probe(struct platform_device *pdev) 184 { 185 struct sama5d2_piobu *piobu; 186 int ret, i; 187 188 piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL); 189 if (!piobu) 190 return -ENOMEM; 191 192 piobu->chip.label = pdev->name; 193 piobu->chip.parent = &pdev->dev; 194 piobu->chip.owner = THIS_MODULE; 195 piobu->chip.get_direction = sama5d2_piobu_get_direction; 196 piobu->chip.direction_input = sama5d2_piobu_direction_input; 197 piobu->chip.direction_output = sama5d2_piobu_direction_output; 198 piobu->chip.get = sama5d2_piobu_get; 199 piobu->chip.set = sama5d2_piobu_set; 200 piobu->chip.base = -1; 201 piobu->chip.ngpio = PIOBU_NUM; 202 piobu->chip.can_sleep = 0; 203 204 piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node); 205 if (IS_ERR(piobu->regmap)) { 206 dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n", 207 PTR_ERR(piobu->regmap)); 208 return PTR_ERR(piobu->regmap); 209 } 210 211 ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu); 212 if (ret) { 213 dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret); 214 return ret; 215 } 216 217 for (i = 0; i < PIOBU_NUM; ++i) { 218 ret = sama5d2_piobu_setup_pin(&piobu->chip, i); 219 if (ret) { 220 dev_err(&pdev->dev, "Failed to setup pin: %d %d\n", 221 i, ret); 222 return ret; 223 } 224 } 225 226 return 0; 227 } 228 229 static const struct of_device_id sama5d2_piobu_ids[] = { 230 { .compatible = "atmel,sama5d2-secumod" }, 231 {}, 232 }; 233 MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids); 234 235 static struct platform_driver sama5d2_piobu_driver = { 236 .driver = { 237 .name = "sama5d2-piobu", 238 .of_match_table = sama5d2_piobu_ids, 239 }, 240 .probe = sama5d2_piobu_probe, 241 }; 242 243 module_platform_driver(sama5d2_piobu_driver); 244 245 MODULE_LICENSE("GPL v2"); 246 MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver"); 247 MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>"); 248