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