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-it87.c (9170B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  GPIO interface for IT87xx Super I/O chips
      4 *
      5 *  Author: Diego Elio Pettenò <flameeyes@flameeyes.eu>
      6 *  Copyright (c) 2017 Google, Inc.
      7 *
      8 *  Based on it87_wdt.c     by Oliver Schuster
      9 *           gpio-it8761e.c by Denis Turischev
     10 *           gpio-stmpe.c   by Rabin Vincent
     11 */
     12
     13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     14
     15#include <linux/init.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/io.h>
     19#include <linux/errno.h>
     20#include <linux/ioport.h>
     21#include <linux/slab.h>
     22#include <linux/gpio/driver.h>
     23
     24/* Chip Id numbers */
     25#define NO_DEV_ID	0xffff
     26#define IT8613_ID	0x8613
     27#define IT8620_ID	0x8620
     28#define IT8628_ID	0x8628
     29#define IT8718_ID       0x8718
     30#define IT8728_ID	0x8728
     31#define IT8732_ID	0x8732
     32#define IT8761_ID	0x8761
     33#define IT8772_ID	0x8772
     34#define IT8786_ID	0x8786
     35
     36/* IO Ports */
     37#define REG		0x2e
     38#define VAL		0x2f
     39
     40/* Logical device Numbers LDN */
     41#define GPIO		0x07
     42
     43/* Configuration Registers and Functions */
     44#define LDNREG		0x07
     45#define CHIPID		0x20
     46#define CHIPREV		0x22
     47
     48/**
     49 * struct it87_gpio - it87-specific GPIO chip
     50 * @chip: the underlying gpio_chip structure
     51 * @lock: a lock to avoid races between operations
     52 * @io_base: base address for gpio ports
     53 * @io_size: size of the port rage starting from io_base.
     54 * @output_base: Super I/O register address for Output Enable register
     55 * @simple_base: Super I/O 'Simple I/O' Enable register
     56 * @simple_size: Super IO 'Simple I/O' Enable register size; this is
     57 *	required because IT87xx chips might only provide Simple I/O
     58 *	switches on a subset of lines, whereas the others keep the
     59 *	same status all time.
     60 */
     61struct it87_gpio {
     62	struct gpio_chip chip;
     63	spinlock_t lock;
     64	u16 io_base;
     65	u16 io_size;
     66	u8 output_base;
     67	u8 simple_base;
     68	u8 simple_size;
     69};
     70
     71static struct it87_gpio it87_gpio_chip = {
     72	.lock = __SPIN_LOCK_UNLOCKED(it87_gpio_chip.lock),
     73};
     74
     75/* Superio chip access functions; copied from wdt_it87 */
     76
     77static inline int superio_enter(void)
     78{
     79	/*
     80	 * Try to reserve REG and REG + 1 for exclusive access.
     81	 */
     82	if (!request_muxed_region(REG, 2, KBUILD_MODNAME))
     83		return -EBUSY;
     84
     85	outb(0x87, REG);
     86	outb(0x01, REG);
     87	outb(0x55, REG);
     88	outb(0x55, REG);
     89	return 0;
     90}
     91
     92static inline void superio_exit(void)
     93{
     94	outb(0x02, REG);
     95	outb(0x02, VAL);
     96	release_region(REG, 2);
     97}
     98
     99static inline void superio_select(int ldn)
    100{
    101	outb(LDNREG, REG);
    102	outb(ldn, VAL);
    103}
    104
    105static inline int superio_inb(int reg)
    106{
    107	outb(reg, REG);
    108	return inb(VAL);
    109}
    110
    111static inline void superio_outb(int val, int reg)
    112{
    113	outb(reg, REG);
    114	outb(val, VAL);
    115}
    116
    117static inline int superio_inw(int reg)
    118{
    119	int val;
    120
    121	outb(reg++, REG);
    122	val = inb(VAL) << 8;
    123	outb(reg, REG);
    124	val |= inb(VAL);
    125	return val;
    126}
    127
    128static inline void superio_set_mask(int mask, int reg)
    129{
    130	u8 curr_val = superio_inb(reg);
    131	u8 new_val = curr_val | mask;
    132
    133	if (curr_val != new_val)
    134		superio_outb(new_val, reg);
    135}
    136
    137static inline void superio_clear_mask(int mask, int reg)
    138{
    139	u8 curr_val = superio_inb(reg);
    140	u8 new_val = curr_val & ~mask;
    141
    142	if (curr_val != new_val)
    143		superio_outb(new_val, reg);
    144}
    145
    146static int it87_gpio_request(struct gpio_chip *chip, unsigned gpio_num)
    147{
    148	u8 mask, group;
    149	int rc = 0;
    150	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
    151
    152	mask = 1 << (gpio_num % 8);
    153	group = (gpio_num / 8);
    154
    155	spin_lock(&it87_gpio->lock);
    156
    157	rc = superio_enter();
    158	if (rc)
    159		goto exit;
    160
    161	/* not all the IT87xx chips support Simple I/O and not all of
    162	 * them allow all the lines to be set/unset to Simple I/O.
    163	 */
    164	if (group < it87_gpio->simple_size)
    165		superio_set_mask(mask, group + it87_gpio->simple_base);
    166
    167	/* clear output enable, setting the pin to input, as all the
    168	 * newly-exported GPIO interfaces are set to input.
    169	 */
    170	superio_clear_mask(mask, group + it87_gpio->output_base);
    171
    172	superio_exit();
    173
    174exit:
    175	spin_unlock(&it87_gpio->lock);
    176	return rc;
    177}
    178
    179static int it87_gpio_get(struct gpio_chip *chip, unsigned gpio_num)
    180{
    181	u16 reg;
    182	u8 mask;
    183	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
    184
    185	mask = 1 << (gpio_num % 8);
    186	reg = (gpio_num / 8) + it87_gpio->io_base;
    187
    188	return !!(inb(reg) & mask);
    189}
    190
    191static int it87_gpio_direction_in(struct gpio_chip *chip, unsigned gpio_num)
    192{
    193	u8 mask, group;
    194	int rc = 0;
    195	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
    196
    197	mask = 1 << (gpio_num % 8);
    198	group = (gpio_num / 8);
    199
    200	spin_lock(&it87_gpio->lock);
    201
    202	rc = superio_enter();
    203	if (rc)
    204		goto exit;
    205
    206	/* clear the output enable bit */
    207	superio_clear_mask(mask, group + it87_gpio->output_base);
    208
    209	superio_exit();
    210
    211exit:
    212	spin_unlock(&it87_gpio->lock);
    213	return rc;
    214}
    215
    216static void it87_gpio_set(struct gpio_chip *chip,
    217			  unsigned gpio_num, int val)
    218{
    219	u8 mask, curr_vals;
    220	u16 reg;
    221	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
    222
    223	mask = 1 << (gpio_num % 8);
    224	reg = (gpio_num / 8) + it87_gpio->io_base;
    225
    226	curr_vals = inb(reg);
    227	if (val)
    228		outb(curr_vals | mask, reg);
    229	else
    230		outb(curr_vals & ~mask, reg);
    231}
    232
    233static int it87_gpio_direction_out(struct gpio_chip *chip,
    234				   unsigned gpio_num, int val)
    235{
    236	u8 mask, group;
    237	int rc = 0;
    238	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
    239
    240	mask = 1 << (gpio_num % 8);
    241	group = (gpio_num / 8);
    242
    243	spin_lock(&it87_gpio->lock);
    244
    245	rc = superio_enter();
    246	if (rc)
    247		goto exit;
    248
    249	/* set the output enable bit */
    250	superio_set_mask(mask, group + it87_gpio->output_base);
    251
    252	it87_gpio_set(chip, gpio_num, val);
    253
    254	superio_exit();
    255
    256exit:
    257	spin_unlock(&it87_gpio->lock);
    258	return rc;
    259}
    260
    261static const struct gpio_chip it87_template_chip = {
    262	.label			= KBUILD_MODNAME,
    263	.owner			= THIS_MODULE,
    264	.request		= it87_gpio_request,
    265	.get			= it87_gpio_get,
    266	.direction_input	= it87_gpio_direction_in,
    267	.set			= it87_gpio_set,
    268	.direction_output	= it87_gpio_direction_out,
    269	.base			= -1
    270};
    271
    272static int __init it87_gpio_init(void)
    273{
    274	int rc = 0, i;
    275	u16 chip_type;
    276	u8 chip_rev, gpio_ba_reg;
    277	char *labels, **labels_table;
    278
    279	struct it87_gpio *it87_gpio = &it87_gpio_chip;
    280
    281	rc = superio_enter();
    282	if (rc)
    283		return rc;
    284
    285	chip_type = superio_inw(CHIPID);
    286	chip_rev  = superio_inb(CHIPREV) & 0x0f;
    287	superio_exit();
    288
    289	it87_gpio->chip = it87_template_chip;
    290
    291	switch (chip_type) {
    292	case IT8613_ID:
    293		gpio_ba_reg = 0x62;
    294		it87_gpio->io_size = 8;  /* it8613 only needs 6, use 8 for alignment */
    295		it87_gpio->output_base = 0xc8;
    296		it87_gpio->simple_base = 0xc0;
    297		it87_gpio->simple_size = 6;
    298		it87_gpio->chip.ngpio = 64;  /* has 48, use 64 for convenient calc */
    299		break;
    300	case IT8620_ID:
    301	case IT8628_ID:
    302		gpio_ba_reg = 0x62;
    303		it87_gpio->io_size = 11;
    304		it87_gpio->output_base = 0xc8;
    305		it87_gpio->simple_size = 0;
    306		it87_gpio->chip.ngpio = 64;
    307		break;
    308	case IT8718_ID:
    309	case IT8728_ID:
    310	case IT8732_ID:
    311	case IT8772_ID:
    312	case IT8786_ID:
    313		gpio_ba_reg = 0x62;
    314		it87_gpio->io_size = 8;
    315		it87_gpio->output_base = 0xc8;
    316		it87_gpio->simple_base = 0xc0;
    317		it87_gpio->simple_size = 5;
    318		it87_gpio->chip.ngpio = 64;
    319		break;
    320	case IT8761_ID:
    321		gpio_ba_reg = 0x60;
    322		it87_gpio->io_size = 4;
    323		it87_gpio->output_base = 0xf0;
    324		it87_gpio->simple_size = 0;
    325		it87_gpio->chip.ngpio = 16;
    326		break;
    327	case NO_DEV_ID:
    328		pr_err("no device\n");
    329		return -ENODEV;
    330	default:
    331		pr_err("Unknown Chip found, Chip %04x Revision %x\n",
    332		       chip_type, chip_rev);
    333		return -ENODEV;
    334	}
    335
    336	rc = superio_enter();
    337	if (rc)
    338		return rc;
    339
    340	superio_select(GPIO);
    341
    342	/* fetch GPIO base address */
    343	it87_gpio->io_base = superio_inw(gpio_ba_reg);
    344
    345	superio_exit();
    346
    347	pr_info("Found Chip IT%04x rev %x. %u GPIO lines starting at %04xh\n",
    348		chip_type, chip_rev, it87_gpio->chip.ngpio,
    349		it87_gpio->io_base);
    350
    351	if (!request_region(it87_gpio->io_base, it87_gpio->io_size,
    352							KBUILD_MODNAME))
    353		return -EBUSY;
    354
    355	/* Set up aliases for the GPIO connection.
    356	 *
    357	 * ITE documentation for recent chips such as the IT8728F
    358	 * refers to the GPIO lines as GPxy, with a coordinates system
    359	 * where x is the GPIO group (starting from 1) and y is the
    360	 * bit within the group.
    361	 *
    362	 * By creating these aliases, we make it easier to understand
    363	 * to which GPIO pin we're referring to.
    364	 */
    365	labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
    366								GFP_KERNEL);
    367	labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
    368								GFP_KERNEL);
    369
    370	if (!labels || !labels_table) {
    371		rc = -ENOMEM;
    372		goto labels_free;
    373	}
    374
    375	for (i = 0; i < it87_gpio->chip.ngpio; i++) {
    376		char *label = &labels[i * sizeof("it87_gpXY")];
    377
    378		sprintf(label, "it87_gp%u%u", 1+(i/8), i%8);
    379		labels_table[i] = label;
    380	}
    381
    382	it87_gpio->chip.names = (const char *const*)labels_table;
    383
    384	rc = gpiochip_add_data(&it87_gpio->chip, it87_gpio);
    385	if (rc)
    386		goto labels_free;
    387
    388	return 0;
    389
    390labels_free:
    391	kfree(labels_table);
    392	kfree(labels);
    393	release_region(it87_gpio->io_base, it87_gpio->io_size);
    394	return rc;
    395}
    396
    397static void __exit it87_gpio_exit(void)
    398{
    399	struct it87_gpio *it87_gpio = &it87_gpio_chip;
    400
    401	gpiochip_remove(&it87_gpio->chip);
    402	release_region(it87_gpio->io_base, it87_gpio->io_size);
    403	kfree(it87_gpio->chip.names[0]);
    404	kfree(it87_gpio->chip.names);
    405}
    406
    407module_init(it87_gpio_init);
    408module_exit(it87_gpio_exit);
    409
    410MODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>");
    411MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
    412MODULE_LICENSE("GPL");