File Index
Files and directories
Sign in
FILE

barco-p50-gpio.c

File preview with syntax highlighting.

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