summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-zynqmp-modepin.c
blob: 2f3c9ebfa78d1d6bc4624a614c316e4fbeae2aff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// SPDX-License-Identifier: GPL-2.0
/*
 * Driver for the ps-mode pin configuration.
 *
 * Copyright (c) 2021 Xilinx, Inc.
 */

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/firmware/xlnx-zynqmp.h>

/* 4-bit boot mode pins */
#define MODE_PINS			4

/**
 * modepin_gpio_get_value - Get the state of the specified pin of GPIO device
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 *
 * This function reads the state of the specified pin of the GPIO device.
 *
 * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured
 *         or error value.
 */
static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
{
	u32 regval = 0;
	int ret;

	ret = zynqmp_pm_bootmode_read(&regval);
	if (ret)
		return ret;

	/* When [0:3] corresponding bit is set, then read output bit [8:11],
	 * if the bit is clear then read input bit [4:7] for status or value.
	 */
	if (regval & BIT(pin))
		return !!(regval & BIT(pin + 8));
	else
		return !!(regval & BIT(pin + 4));
}

/**
 * modepin_gpio_set_value - Modify the state of the pin with specified value
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 * @state:	value used to modify the state of the specified pin
 *
 * This function reads the state of the specified pin of the GPIO device, mask
 * with the capture state of GPIO pin, and update pin of GPIO device.
 *
 * Return:	None.
 */
static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
				   int state)
{
	u32 bootpin_val = 0;
	int ret;

	zynqmp_pm_bootmode_read(&bootpin_val);

	/* Configure pin as an output by set bit [0:3] */
	bootpin_val |= BIT(pin);

	if (state)
		bootpin_val |= BIT(pin + 8);
	else
		bootpin_val &= ~BIT(pin + 8);

	/* Configure bootpin value */
	ret = zynqmp_pm_bootmode_write(bootpin_val);
	if (ret)
		pr_err("modepin: set value error %d for pin %d\n", ret, pin);
}

/**
 * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 *
 * Return: 0 always
 */
static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
{
	return 0;
}

/**
 * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output
 * @chip:	gpio_chip instance to be worked on
 * @pin:	gpio pin number within the device
 * @state:	value to be written to specified pin
 *
 * Return: 0 always
 */
static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
				int state)
{
	return 0;
}

/**
 * modepin_gpio_probe - Initialization method for modepin_gpio
 * @pdev:		platform device instance
 *
 * Return: 0 on success, negative error otherwise.
 */
static int modepin_gpio_probe(struct platform_device *pdev)
{
	struct gpio_chip *chip;
	int status;

	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	platform_set_drvdata(pdev, chip);

	/* configure the gpio chip */
	chip->base = -1;
	chip->ngpio = MODE_PINS;
	chip->owner = THIS_MODULE;
	chip->parent = &pdev->dev;
	chip->get = modepin_gpio_get_value;
	chip->set = modepin_gpio_set_value;
	chip->direction_input = modepin_gpio_dir_in;
	chip->direction_output = modepin_gpio_dir_out;
	chip->label = dev_name(&pdev->dev);

	/* modepin gpio registration */
	status = devm_gpiochip_add_data(&pdev->dev, chip, chip);
	if (status)
		return dev_err_probe(&pdev->dev, status,
			      "Failed to add GPIO chip\n");

	return status;
}

static const struct of_device_id modepin_platform_id[] = {
	{ .compatible = "xlnx,zynqmp-gpio-modepin", },
	{ }
};
MODULE_DEVICE_TABLE(of, modepin_platform_id);

static struct platform_driver modepin_platform_driver = {
	.driver = {
		.name = "modepin-gpio",
		.of_match_table = modepin_platform_id,
	},
	.probe = modepin_gpio_probe,
};

module_platform_driver(modepin_platform_driver);

MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>");
MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration");
MODULE_LICENSE("GPL v2");