cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gpio-sama5d2-piobu.c (6147B)


      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
     47struct 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 */
     58static 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 */
     79static 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 */
     95static 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 */
    114static 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) ? GPIO_LINE_DIRECTION_IN :
    123				   GPIO_LINE_DIRECTION_OUT;
    124}
    125
    126/*
    127 * sama5d2_piobu_direction_input() - gpiochip direction_input
    128 */
    129static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
    130					 unsigned int pin)
    131{
    132	return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
    133}
    134
    135/*
    136 * sama5d2_piobu_direction_output() - gpiochip direction_output
    137 */
    138static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
    139					  unsigned int pin, int value)
    140{
    141	unsigned int val = PIOBU_OUT;
    142
    143	if (value)
    144		val |= PIOBU_HIGH;
    145
    146	return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD,
    147					 val);
    148}
    149
    150/*
    151 * sama5d2_piobu_get() - gpiochip get
    152 */
    153static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
    154{
    155	/* if pin is input, read value from PDS else read from SOD */
    156	int ret = sama5d2_piobu_get_direction(chip, pin);
    157
    158	if (ret == GPIO_LINE_DIRECTION_IN)
    159		ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
    160	else if (ret == GPIO_LINE_DIRECTION_OUT)
    161		ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
    162
    163	if (ret < 0)
    164		return ret;
    165
    166	return !!ret;
    167}
    168
    169/*
    170 * sama5d2_piobu_set() - gpiochip set
    171 */
    172static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
    173			      int value)
    174{
    175	if (!value)
    176		value = PIOBU_LOW;
    177	else
    178		value = PIOBU_HIGH;
    179
    180	sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
    181}
    182
    183static int sama5d2_piobu_probe(struct platform_device *pdev)
    184{
    185	struct sama5d2_piobu *piobu;
    186	int ret, i;
    187
    188	piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
    189	if (!piobu)
    190		return -ENOMEM;
    191
    192	platform_set_drvdata(pdev, piobu);
    193	piobu->chip.label = pdev->name;
    194	piobu->chip.parent = &pdev->dev;
    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
    230static const struct of_device_id sama5d2_piobu_ids[] = {
    231	{ .compatible = "atmel,sama5d2-secumod" },
    232	{},
    233};
    234MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
    235
    236static 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
    244module_platform_driver(sama5d2_piobu_driver);
    245
    246MODULE_LICENSE("GPL v2");
    247MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
    248MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");