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_set_direction() - mark pin as input or output 113 */ 114 static int sama5d2_piobu_set_direction(struct gpio_chip *chip, 115 unsigned int direction, 116 unsigned int pin) 117 { 118 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction); 119 } 120 121 /** 122 * sama5d2_piobu_get_direction() - gpiochip get_direction 123 */ 124 static int sama5d2_piobu_get_direction(struct gpio_chip *chip, 125 unsigned int pin) 126 { 127 int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION); 128 129 if (ret < 0) 130 return ret; 131 132 return (ret == PIOBU_IN) ? 1 : 0; 133 } 134 135 /** 136 * sama5d2_piobu_direction_input() - gpiochip direction_input 137 */ 138 static int sama5d2_piobu_direction_input(struct gpio_chip *chip, 139 unsigned int pin) 140 { 141 return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin); 142 } 143 144 /** 145 * sama5d2_piobu_direction_output() - gpiochip direction_output 146 */ 147 static int sama5d2_piobu_direction_output(struct gpio_chip *chip, 148 unsigned int pin, int value) 149 { 150 return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin); 151 } 152 153 /** 154 * sama5d2_piobu_get() - gpiochip get 155 */ 156 static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) 157 { 158 /* if pin is input, read value from PDS else read from SOD */ 159 int ret = sama5d2_piobu_get_direction(chip, pin); 160 161 if (ret == 1) 162 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS); 163 else if (!ret) 164 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD); 165 166 if (ret < 0) 167 return ret; 168 169 return !!ret; 170 } 171 172 /** 173 * sama5d2_piobu_set() - gpiochip set 174 */ 175 static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, 176 int value) 177 { 178 if (!value) 179 value = PIOBU_LOW; 180 else 181 value = PIOBU_HIGH; 182 183 sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); 184 } 185 186 static int sama5d2_piobu_probe(struct platform_device *pdev) 187 { 188 struct sama5d2_piobu *piobu; 189 int ret, i; 190 191 piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL); 192 if (!piobu) 193 return -ENOMEM; 194 195 platform_set_drvdata(pdev, piobu); 196 piobu->chip.label = pdev->name; 197 piobu->chip.parent = &pdev->dev; 198 piobu->chip.of_node = pdev->dev.of_node; 199 piobu->chip.owner = THIS_MODULE, 200 piobu->chip.get_direction = sama5d2_piobu_get_direction, 201 piobu->chip.direction_input = sama5d2_piobu_direction_input, 202 piobu->chip.direction_output = sama5d2_piobu_direction_output, 203 piobu->chip.get = sama5d2_piobu_get, 204 piobu->chip.set = sama5d2_piobu_set, 205 piobu->chip.base = -1, 206 piobu->chip.ngpio = PIOBU_NUM, 207 piobu->chip.can_sleep = 0, 208 209 piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node); 210 if (IS_ERR(piobu->regmap)) { 211 dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n", 212 PTR_ERR(piobu->regmap)); 213 return PTR_ERR(piobu->regmap); 214 } 215 216 ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu); 217 if (ret) { 218 dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret); 219 return ret; 220 } 221 222 for (i = 0; i < PIOBU_NUM; ++i) { 223 ret = sama5d2_piobu_setup_pin(&piobu->chip, i); 224 if (ret) { 225 dev_err(&pdev->dev, "Failed to setup pin: %d %d\n", 226 i, ret); 227 return ret; 228 } 229 } 230 231 return 0; 232 } 233 234 static const struct of_device_id sama5d2_piobu_ids[] = { 235 { .compatible = "atmel,sama5d2-secumod" }, 236 {}, 237 }; 238 MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids); 239 240 static struct platform_driver sama5d2_piobu_driver = { 241 .driver = { 242 .name = "sama5d2-piobu", 243 .of_match_table = of_match_ptr(sama5d2_piobu_ids) 244 }, 245 .probe = sama5d2_piobu_probe, 246 }; 247 248 module_platform_driver(sama5d2_piobu_driver); 249 250 MODULE_VERSION("1.0"); 251 MODULE_LICENSE("GPL v2"); 252 MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver"); 253 MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>"); 254