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-mm-lantiq.c (3981B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *
      4 *  Copyright (C) 2012 John Crispin <john@phrozen.org>
      5 */
      6
      7#include <linux/init.h>
      8#include <linux/module.h>
      9#include <linux/types.h>
     10#include <linux/platform_device.h>
     11#include <linux/mutex.h>
     12#include <linux/gpio/driver.h>
     13#include <linux/of.h>
     14#include <linux/of_gpio.h>
     15#include <linux/io.h>
     16#include <linux/slab.h>
     17
     18#include <lantiq_soc.h>
     19
     20/*
     21 * By attaching hardware latches to the EBU it is possible to create output
     22 * only gpios. This driver configures a special memory address, which when
     23 * written to outputs 16 bit to the latches.
     24 */
     25
     26#define LTQ_EBU_BUSCON	0x1e7ff		/* 16 bit access, slowest timing */
     27#define LTQ_EBU_WP	0x80000000	/* write protect bit */
     28
     29struct ltq_mm {
     30	struct of_mm_gpio_chip mmchip;
     31	u16 shadow;	/* shadow the latches state */
     32};
     33
     34/**
     35 * ltq_mm_apply() - write the shadow value to the ebu address.
     36 * @chip:     Pointer to our private data structure.
     37 *
     38 * Write the shadow value to the EBU to set the gpios. We need to set the
     39 * global EBU lock to make sure that PCI/MTD don't break.
     40 */
     41static void ltq_mm_apply(struct ltq_mm *chip)
     42{
     43	unsigned long flags;
     44
     45	spin_lock_irqsave(&ebu_lock, flags);
     46	ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
     47	__raw_writew(chip->shadow, chip->mmchip.regs);
     48	ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
     49	spin_unlock_irqrestore(&ebu_lock, flags);
     50}
     51
     52/**
     53 * ltq_mm_set() - gpio_chip->set - set gpios.
     54 * @gc:     Pointer to gpio_chip device structure.
     55 * @gpio:   GPIO signal number.
     56 * @val:    Value to be written to specified signal.
     57 *
     58 * Set the shadow value and call ltq_mm_apply.
     59 */
     60static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value)
     61{
     62	struct ltq_mm *chip = gpiochip_get_data(gc);
     63
     64	if (value)
     65		chip->shadow |= (1 << offset);
     66	else
     67		chip->shadow &= ~(1 << offset);
     68	ltq_mm_apply(chip);
     69}
     70
     71/**
     72 * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
     73 * @gc:     Pointer to gpio_chip device structure.
     74 * @gpio:   GPIO signal number.
     75 * @val:    Value to be written to specified signal.
     76 *
     77 * Same as ltq_mm_set, always returns 0.
     78 */
     79static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)
     80{
     81	ltq_mm_set(gc, offset, value);
     82
     83	return 0;
     84}
     85
     86/**
     87 * ltq_mm_save_regs() - Set initial values of GPIO pins
     88 * @mm_gc: pointer to memory mapped GPIO chip structure
     89 */
     90static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc)
     91{
     92	struct ltq_mm *chip =
     93		container_of(mm_gc, struct ltq_mm, mmchip);
     94
     95	/* tell the ebu controller which memory address we will be using */
     96	ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1);
     97
     98	ltq_mm_apply(chip);
     99}
    100
    101static int ltq_mm_probe(struct platform_device *pdev)
    102{
    103	struct ltq_mm *chip;
    104	u32 shadow;
    105
    106	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
    107	if (!chip)
    108		return -ENOMEM;
    109
    110	platform_set_drvdata(pdev, chip);
    111
    112	chip->mmchip.gc.ngpio = 16;
    113	chip->mmchip.gc.direction_output = ltq_mm_dir_out;
    114	chip->mmchip.gc.set = ltq_mm_set;
    115	chip->mmchip.save_regs = ltq_mm_save_regs;
    116
    117	/* store the shadow value if one was passed by the devicetree */
    118	if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
    119		chip->shadow = shadow;
    120
    121	return of_mm_gpiochip_add_data(pdev->dev.of_node, &chip->mmchip, chip);
    122}
    123
    124static int ltq_mm_remove(struct platform_device *pdev)
    125{
    126	struct ltq_mm *chip = platform_get_drvdata(pdev);
    127
    128	of_mm_gpiochip_remove(&chip->mmchip);
    129
    130	return 0;
    131}
    132
    133static const struct of_device_id ltq_mm_match[] = {
    134	{ .compatible = "lantiq,gpio-mm" },
    135	{},
    136};
    137MODULE_DEVICE_TABLE(of, ltq_mm_match);
    138
    139static struct platform_driver ltq_mm_driver = {
    140	.probe = ltq_mm_probe,
    141	.remove = ltq_mm_remove,
    142	.driver = {
    143		.name = "gpio-mm-ltq",
    144		.of_match_table = ltq_mm_match,
    145	},
    146};
    147
    148static int __init ltq_mm_init(void)
    149{
    150	return platform_driver_register(&ltq_mm_driver);
    151}
    152
    153subsys_initcall(ltq_mm_init);
    154
    155static void __exit ltq_mm_exit(void)
    156{
    157	platform_driver_unregister(&ltq_mm_driver);
    158}
    159module_exit(ltq_mm_exit);