Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SAMA5D2 PIOBU GPIO controller
0004  *
0005  * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
0006  *
0007  * Author: Andrei Stefanescu <andrei.stefanescu@microchip.com>
0008  *
0009  */
0010 #include <linux/bits.h>
0011 #include <linux/gpio/driver.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/mfd/syscon.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regmap.h>
0019 
0020 #define PIOBU_NUM 8
0021 #define PIOBU_REG_SIZE 4
0022 
0023 /*
0024  * backup mode protection register for tamper detection
0025  * normal mode protection register for tamper detection
0026  * wakeup signal generation
0027  */
0028 #define PIOBU_BMPR 0x7C
0029 #define PIOBU_NMPR 0x80
0030 #define PIOBU_WKPR 0x90
0031 
0032 #define PIOBU_BASE 0x18 /* PIOBU offset from SECUMOD base register address. */
0033 
0034 #define PIOBU_DET_OFFSET 16
0035 
0036 /* In the datasheet this bit is called OUTPUT */
0037 #define PIOBU_DIRECTION BIT(8)
0038 #define PIOBU_OUT BIT(8)
0039 #define PIOBU_IN 0
0040 
0041 #define PIOBU_SOD BIT(9)
0042 #define PIOBU_PDS BIT(10)
0043 
0044 #define PIOBU_HIGH BIT(9)
0045 #define PIOBU_LOW 0
0046 
0047 struct sama5d2_piobu {
0048     struct gpio_chip chip;
0049     struct regmap *regmap;
0050 };
0051 
0052 /*
0053  * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
0054  *
0055  * Do not consider pin for tamper detection (normal and backup modes)
0056  * Do not consider pin as tamper wakeup interrupt source
0057  */
0058 static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
0059 {
0060     int ret;
0061     struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0062                            chip);
0063     unsigned int mask = BIT(PIOBU_DET_OFFSET + pin);
0064 
0065     ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0);
0066     if (ret)
0067         return ret;
0068 
0069     ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0);
0070     if (ret)
0071         return ret;
0072 
0073     return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
0074 }
0075 
0076 /*
0077  * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
0078  */
0079 static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
0080                      unsigned int mask, unsigned int value)
0081 {
0082     int reg;
0083     struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0084                            chip);
0085 
0086     reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
0087 
0088     return regmap_update_bits(piobu->regmap, reg, mask, value);
0089 }
0090 
0091 /*
0092  * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
0093  *                register
0094  */
0095 static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
0096                     unsigned int mask)
0097 {
0098     struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0099                            chip);
0100     unsigned int val, reg;
0101     int ret;
0102 
0103     reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
0104     ret = regmap_read(piobu->regmap, reg, &val);
0105     if (ret < 0)
0106         return ret;
0107 
0108     return val & mask;
0109 }
0110 
0111 /*
0112  * sama5d2_piobu_get_direction() - gpiochip get_direction
0113  */
0114 static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
0115                        unsigned int pin)
0116 {
0117     int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION);
0118 
0119     if (ret < 0)
0120         return ret;
0121 
0122     return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN :
0123                    GPIO_LINE_DIRECTION_OUT;
0124 }
0125 
0126 /*
0127  * sama5d2_piobu_direction_input() - gpiochip direction_input
0128  */
0129 static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
0130                      unsigned int pin)
0131 {
0132     return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
0133 }
0134 
0135 /*
0136  * sama5d2_piobu_direction_output() - gpiochip direction_output
0137  */
0138 static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
0139                       unsigned int pin, int value)
0140 {
0141     unsigned int val = PIOBU_OUT;
0142 
0143     if (value)
0144         val |= PIOBU_HIGH;
0145 
0146     return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD,
0147                      val);
0148 }
0149 
0150 /*
0151  * sama5d2_piobu_get() - gpiochip get
0152  */
0153 static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
0154 {
0155     /* if pin is input, read value from PDS else read from SOD */
0156     int ret = sama5d2_piobu_get_direction(chip, pin);
0157 
0158     if (ret == GPIO_LINE_DIRECTION_IN)
0159         ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
0160     else if (ret == GPIO_LINE_DIRECTION_OUT)
0161         ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
0162 
0163     if (ret < 0)
0164         return ret;
0165 
0166     return !!ret;
0167 }
0168 
0169 /*
0170  * sama5d2_piobu_set() - gpiochip set
0171  */
0172 static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
0173                   int value)
0174 {
0175     if (!value)
0176         value = PIOBU_LOW;
0177     else
0178         value = PIOBU_HIGH;
0179 
0180     sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
0181 }
0182 
0183 static int sama5d2_piobu_probe(struct platform_device *pdev)
0184 {
0185     struct sama5d2_piobu *piobu;
0186     int ret, i;
0187 
0188     piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
0189     if (!piobu)
0190         return -ENOMEM;
0191 
0192     platform_set_drvdata(pdev, piobu);
0193     piobu->chip.label = pdev->name;
0194     piobu->chip.parent = &pdev->dev;
0195     piobu->chip.owner = THIS_MODULE,
0196     piobu->chip.get_direction = sama5d2_piobu_get_direction,
0197     piobu->chip.direction_input = sama5d2_piobu_direction_input,
0198     piobu->chip.direction_output = sama5d2_piobu_direction_output,
0199     piobu->chip.get = sama5d2_piobu_get,
0200     piobu->chip.set = sama5d2_piobu_set,
0201     piobu->chip.base = -1,
0202     piobu->chip.ngpio = PIOBU_NUM,
0203     piobu->chip.can_sleep = 0,
0204 
0205     piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
0206     if (IS_ERR(piobu->regmap)) {
0207         dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n",
0208             PTR_ERR(piobu->regmap));
0209         return PTR_ERR(piobu->regmap);
0210     }
0211 
0212     ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu);
0213     if (ret) {
0214         dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret);
0215         return ret;
0216     }
0217 
0218     for (i = 0; i < PIOBU_NUM; ++i) {
0219         ret = sama5d2_piobu_setup_pin(&piobu->chip, i);
0220         if (ret) {
0221             dev_err(&pdev->dev, "Failed to setup pin: %d %d\n",
0222                 i, ret);
0223             return ret;
0224         }
0225     }
0226 
0227     return 0;
0228 }
0229 
0230 static const struct of_device_id sama5d2_piobu_ids[] = {
0231     { .compatible = "atmel,sama5d2-secumod" },
0232     {},
0233 };
0234 MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
0235 
0236 static struct platform_driver sama5d2_piobu_driver = {
0237     .driver = {
0238         .name       = "sama5d2-piobu",
0239         .of_match_table = of_match_ptr(sama5d2_piobu_ids)
0240     },
0241     .probe = sama5d2_piobu_probe,
0242 };
0243 
0244 module_platform_driver(sama5d2_piobu_driver);
0245 
0246 MODULE_LICENSE("GPL v2");
0247 MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
0248 MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");