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