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