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

barco-p50-gpio.c (9376B)


      1// SPDX-License-Identifier: GPL-2.0+
      2
      3/*
      4 * Support for EC-connected GPIOs for identify
      5 * LED/button on Barco P50 board
      6 *
      7 * Copyright (C) 2021 Barco NV
      8 * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
      9 */
     10
     11#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
     12
     13#include <linux/delay.h>
     14#include <linux/dmi.h>
     15#include <linux/err.h>
     16#include <linux/io.h>
     17#include <linux/kernel.h>
     18#include <linux/leds.h>
     19#include <linux/module.h>
     20#include <linux/platform_device.h>
     21#include <linux/gpio_keys.h>
     22#include <linux/gpio/driver.h>
     23#include <linux/gpio/machine.h>
     24#include <linux/input.h>
     25
     26
     27#define DRIVER_NAME		"barco-p50-gpio"
     28
     29/* GPIO lines */
     30#define P50_GPIO_LINE_LED	0
     31#define P50_GPIO_LINE_BTN	1
     32
     33/* GPIO IO Ports */
     34#define P50_GPIO_IO_PORT_BASE	0x299
     35
     36#define P50_PORT_DATA		0x00
     37#define P50_PORT_CMD		0x01
     38
     39#define P50_STATUS_OBF		0x01 /* EC output buffer full */
     40#define P50_STATUS_IBF		0x02 /* EC input buffer full */
     41
     42#define P50_CMD_READ		0xa0
     43#define P50_CMD_WRITE		0x50
     44
     45/* EC mailbox registers */
     46#define P50_MBOX_REG_CMD	0x00
     47#define P50_MBOX_REG_STATUS	0x01
     48#define P50_MBOX_REG_PARAM	0x02
     49#define P50_MBOX_REG_DATA	0x03
     50
     51#define P50_MBOX_CMD_READ_GPIO	0x11
     52#define P50_MBOX_CMD_WRITE_GPIO	0x12
     53#define P50_MBOX_CMD_CLEAR	0xff
     54
     55#define P50_MBOX_STATUS_SUCCESS	0x01
     56
     57#define P50_MBOX_PARAM_LED	0x12
     58#define P50_MBOX_PARAM_BTN	0x13
     59
     60
     61struct p50_gpio {
     62	struct gpio_chip gc;
     63	struct mutex lock;
     64	unsigned long base;
     65	struct platform_device *leds_pdev;
     66	struct platform_device *keys_pdev;
     67};
     68
     69static struct platform_device *gpio_pdev;
     70
     71static int gpio_params[] = {
     72	[P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
     73	[P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
     74};
     75
     76static const char * const gpio_names[] = {
     77	[P50_GPIO_LINE_LED] = "identify-led",
     78	[P50_GPIO_LINE_BTN] = "identify-button",
     79};
     80
     81
     82static struct gpiod_lookup_table p50_gpio_led_table = {
     83	.dev_id = "leds-gpio",
     84	.table = {
     85		GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
     86		{}
     87	}
     88};
     89
     90/* GPIO LEDs */
     91static struct gpio_led leds[] = {
     92	{ .name = "identify" }
     93};
     94
     95static struct gpio_led_platform_data leds_pdata = {
     96	.num_leds = ARRAY_SIZE(leds),
     97	.leds = leds,
     98};
     99
    100/* GPIO keyboard */
    101static struct gpio_keys_button buttons[] = {
    102	{
    103		.code = KEY_VENDOR,
    104		.gpio = P50_GPIO_LINE_BTN,
    105		.active_low = 1,
    106		.type = EV_KEY,
    107		.value = 1,
    108	},
    109};
    110
    111static struct gpio_keys_platform_data keys_pdata = {
    112	.buttons = buttons,
    113	.nbuttons = ARRAY_SIZE(buttons),
    114	.poll_interval = 100,
    115	.rep = 0,
    116	.name = "identify",
    117};
    118
    119
    120/* low level access routines */
    121
    122static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
    123{
    124	int i, val;
    125
    126	for (i = 0; i < 100; i++) {
    127		val = inb(p50->base + P50_PORT_CMD) & mask;
    128		if (val == expected)
    129			return 0;
    130		usleep_range(500, 2000);
    131	}
    132
    133	dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
    134	return -ETIMEDOUT;
    135}
    136
    137
    138static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
    139{
    140	int ret;
    141
    142	ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
    143	if (ret)
    144		return ret;
    145
    146	/* clear output buffer flag, prevent unfinished commands */
    147	inb(p50->base + P50_PORT_DATA);
    148
    149	/* cmd/address */
    150	outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
    151
    152	ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
    153	if (ret)
    154		return ret;
    155
    156	return inb(p50->base + P50_PORT_DATA);
    157}
    158
    159static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
    160{
    161	int ret;
    162
    163	ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
    164	if (ret)
    165		return ret;
    166
    167	/* cmd/address */
    168	outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
    169
    170	ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
    171	if (ret)
    172		return ret;
    173
    174	/* data */
    175	outb(val, p50->base + P50_PORT_DATA);
    176
    177	return 0;
    178}
    179
    180
    181/* mbox routines */
    182
    183static int p50_wait_mbox_idle(struct p50_gpio *p50)
    184{
    185	int i, val;
    186
    187	for (i = 0; i < 1000; i++) {
    188		val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
    189		/* cmd is 0 when idle */
    190		if (val <= 0)
    191			return val;
    192
    193		usleep_range(500, 2000);
    194	}
    195
    196	dev_err(p50->gc.parent,	"Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
    197
    198	return -ETIMEDOUT;
    199}
    200
    201static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
    202{
    203	int ret;
    204
    205	ret = p50_wait_mbox_idle(p50);
    206	if (ret)
    207		return ret;
    208
    209	ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
    210	if (ret)
    211		return ret;
    212
    213	ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
    214	if (ret)
    215		return ret;
    216
    217	ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
    218	if (ret)
    219		return ret;
    220
    221	ret = p50_wait_mbox_idle(p50);
    222	if (ret)
    223		return ret;
    224
    225	ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
    226	if (ret < 0)
    227		return ret;
    228
    229	if (ret == P50_MBOX_STATUS_SUCCESS)
    230		return 0;
    231
    232	dev_err(p50->gc.parent,	"Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
    233		cmd, ret, param, data);
    234
    235	return -EIO;
    236}
    237
    238
    239/* gpio routines */
    240
    241static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
    242{
    243	switch (offset) {
    244	case P50_GPIO_LINE_BTN:
    245		return GPIO_LINE_DIRECTION_IN;
    246
    247	case P50_GPIO_LINE_LED:
    248		return GPIO_LINE_DIRECTION_OUT;
    249
    250	default:
    251		return -EINVAL;
    252	}
    253}
    254
    255static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
    256{
    257	struct p50_gpio *p50 = gpiochip_get_data(gc);
    258	int ret;
    259
    260	mutex_lock(&p50->lock);
    261
    262	ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
    263	if (ret == 0)
    264		ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
    265
    266	mutex_unlock(&p50->lock);
    267
    268	return ret;
    269}
    270
    271static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
    272{
    273	struct p50_gpio *p50 = gpiochip_get_data(gc);
    274
    275	mutex_lock(&p50->lock);
    276
    277	p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
    278
    279	mutex_unlock(&p50->lock);
    280}
    281
    282static int p50_gpio_probe(struct platform_device *pdev)
    283{
    284	struct p50_gpio *p50;
    285	struct resource *res;
    286	int ret;
    287
    288	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
    289	if (!res) {
    290		dev_err(&pdev->dev, "Cannot get I/O ports\n");
    291		return -ENODEV;
    292	}
    293
    294	if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
    295		dev_err(&pdev->dev, "Unable to reserve I/O region\n");
    296		return -EBUSY;
    297	}
    298
    299	p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
    300	if (!p50)
    301		return -ENOMEM;
    302
    303	platform_set_drvdata(pdev, p50);
    304	mutex_init(&p50->lock);
    305	p50->base = res->start;
    306	p50->gc.owner = THIS_MODULE;
    307	p50->gc.parent = &pdev->dev;
    308	p50->gc.label = dev_name(&pdev->dev);
    309	p50->gc.ngpio = ARRAY_SIZE(gpio_names);
    310	p50->gc.names = gpio_names;
    311	p50->gc.can_sleep = true;
    312	p50->gc.base = -1;
    313	p50->gc.get_direction = p50_gpio_get_direction;
    314	p50->gc.get = p50_gpio_get;
    315	p50->gc.set = p50_gpio_set;
    316
    317
    318	/* reset mbox */
    319	ret = p50_wait_mbox_idle(p50);
    320	if (ret)
    321		return ret;
    322
    323	ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR);
    324	if (ret)
    325		return ret;
    326
    327	ret = p50_wait_mbox_idle(p50);
    328	if (ret)
    329		return ret;
    330
    331
    332	ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
    333	if (ret < 0) {
    334		dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
    335		return ret;
    336	}
    337
    338	gpiod_add_lookup_table(&p50_gpio_led_table);
    339
    340	p50->leds_pdev = platform_device_register_data(&pdev->dev,
    341		"leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
    342
    343	if (IS_ERR(p50->leds_pdev)) {
    344		ret = PTR_ERR(p50->leds_pdev);
    345		dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
    346		goto err_leds;
    347	}
    348
    349	/* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
    350	buttons[0].gpio += p50->gc.base;
    351
    352	p50->keys_pdev =
    353		platform_device_register_data(&pdev->dev, "gpio-keys-polled",
    354					      PLATFORM_DEVID_NONE,
    355					      &keys_pdata, sizeof(keys_pdata));
    356
    357	if (IS_ERR(p50->keys_pdev)) {
    358		ret = PTR_ERR(p50->keys_pdev);
    359		dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
    360		goto err_keys;
    361	}
    362
    363	return 0;
    364
    365err_keys:
    366	platform_device_unregister(p50->leds_pdev);
    367err_leds:
    368	gpiod_remove_lookup_table(&p50_gpio_led_table);
    369
    370	return ret;
    371}
    372
    373static int p50_gpio_remove(struct platform_device *pdev)
    374{
    375	struct p50_gpio *p50 = platform_get_drvdata(pdev);
    376
    377	platform_device_unregister(p50->keys_pdev);
    378	platform_device_unregister(p50->leds_pdev);
    379
    380	gpiod_remove_lookup_table(&p50_gpio_led_table);
    381
    382	return 0;
    383}
    384
    385static struct platform_driver p50_gpio_driver = {
    386	.driver = {
    387		.name = DRIVER_NAME,
    388	},
    389	.probe = p50_gpio_probe,
    390	.remove = p50_gpio_remove,
    391};
    392
    393/* Board setup */
    394static const struct dmi_system_id dmi_ids[] __initconst = {
    395	{
    396		.matches = {
    397			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"),
    398			DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
    399		},
    400	},
    401	{}
    402};
    403MODULE_DEVICE_TABLE(dmi, dmi_ids);
    404
    405static int __init p50_module_init(void)
    406{
    407	struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
    408	int ret;
    409
    410	if (!dmi_first_match(dmi_ids))
    411		return -ENODEV;
    412
    413	ret = platform_driver_register(&p50_gpio_driver);
    414	if (ret)
    415		return ret;
    416
    417	gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
    418	if (IS_ERR(gpio_pdev)) {
    419		pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
    420		platform_driver_unregister(&p50_gpio_driver);
    421		return PTR_ERR(gpio_pdev);
    422	}
    423
    424	return 0;
    425}
    426
    427static void __exit p50_module_exit(void)
    428{
    429	platform_device_unregister(gpio_pdev);
    430	platform_driver_unregister(&p50_gpio_driver);
    431}
    432
    433module_init(p50_module_init);
    434module_exit(p50_module_exit);
    435
    436MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
    437MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
    438MODULE_LICENSE("GPL");