brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · af566f7 Raw
437 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Support for EC-connected GPIOs for identify5 * LED/button on Barco P50 board6 *7 * Copyright (C) 2021 Barco NV8 * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>9 */10 11#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt12 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	031#define P50_GPIO_LINE_BTN	132 33/* GPIO IO Ports */34#define P50_GPIO_IO_PORT_BASE	0x29935 36#define P50_PORT_DATA		0x0037#define P50_PORT_CMD		0x0138 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		0xa043#define P50_CMD_WRITE		0x5044 45/* EC mailbox registers */46#define P50_MBOX_REG_CMD	0x0047#define P50_MBOX_REG_STATUS	0x0148#define P50_MBOX_REG_PARAM	0x0249#define P50_MBOX_REG_DATA	0x0350 51#define P50_MBOX_CMD_READ_GPIO	0x1152#define P50_MBOX_CMD_WRITE_GPIO	0x1253#define P50_MBOX_CMD_CLEAR	0xff54 55#define P50_MBOX_STATUS_SUCCESS	0x0156 57#define P50_MBOX_PARAM_LED	0x1258#define P50_MBOX_PARAM_BTN	0x1359 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 void 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 383static struct platform_driver p50_gpio_driver = {384	.driver = {385		.name = DRIVER_NAME,386	},387	.probe = p50_gpio_probe,388	.remove_new = p50_gpio_remove,389};390 391/* Board setup */392static const struct dmi_system_id dmi_ids[] __initconst = {393	{394		.matches = {395			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"),396			DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")397		},398	},399	{}400};401MODULE_DEVICE_TABLE(dmi, dmi_ids);402 403static int __init p50_module_init(void)404{405	struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);406	int ret;407 408	if (!dmi_first_match(dmi_ids))409		return -ENODEV;410 411	ret = platform_driver_register(&p50_gpio_driver);412	if (ret)413		return ret;414 415	gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);416	if (IS_ERR(gpio_pdev)) {417		pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));418		platform_driver_unregister(&p50_gpio_driver);419		return PTR_ERR(gpio_pdev);420	}421 422	return 0;423}424 425static void __exit p50_module_exit(void)426{427	platform_device_unregister(gpio_pdev);428	platform_driver_unregister(&p50_gpio_driver);429}430 431module_init(p50_module_init);432module_exit(p50_module_exit);433 434MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");435MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");436MODULE_LICENSE("GPL");437