xref: /linux/drivers/gpio/gpio-zynqmp-modepin.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for the ps-mode pin configuration.
4  *
5  * Copyright (c) 2021 Xilinx, Inc.
6  */
7 
8 #include <linux/delay.h>
9 #include <linux/err.h>
10 #include <linux/gpio/driver.h>
11 #include <linux/io.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/firmware/xlnx-zynqmp.h>
17 
18 /* 4-bit boot mode pins */
19 #define MODE_PINS			4
20 
21 /**
22  * modepin_gpio_get_value - Get the state of the specified pin of GPIO device
23  * @chip:	gpio_chip instance to be worked on
24  * @pin:	gpio pin number within the device
25  *
26  * This function reads the state of the specified pin of the GPIO device.
27  *
28  * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured
29  *         or error value.
30  */
31 static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
32 {
33 	u32 regval = 0;
34 	int ret;
35 
36 	ret = zynqmp_pm_bootmode_read(&regval);
37 	if (ret)
38 		return ret;
39 
40 	/* When [0:3] corresponding bit is set, then read output bit [8:11],
41 	 * if the bit is clear then read input bit [4:7] for status or value.
42 	 */
43 	if (regval & BIT(pin))
44 		return !!(regval & BIT(pin + 8));
45 	else
46 		return !!(regval & BIT(pin + 4));
47 }
48 
49 /**
50  * modepin_gpio_set_value - Modify the state of the pin with specified value
51  * @chip:	gpio_chip instance to be worked on
52  * @pin:	gpio pin number within the device
53  * @state:	value used to modify the state of the specified pin
54  *
55  * This function reads the state of the specified pin of the GPIO device, mask
56  * with the capture state of GPIO pin, and update pin of GPIO device.
57  *
58  * Return:	None.
59  */
60 static int modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
61 				  int state)
62 {
63 	u32 bootpin_val = 0;
64 	int ret;
65 
66 	zynqmp_pm_bootmode_read(&bootpin_val);
67 
68 	/* Configure pin as an output by set bit [0:3] */
69 	bootpin_val |= BIT(pin);
70 
71 	if (state)
72 		bootpin_val |= BIT(pin + 8);
73 	else
74 		bootpin_val &= ~BIT(pin + 8);
75 
76 	/* Configure bootpin value */
77 	ret = zynqmp_pm_bootmode_write(bootpin_val);
78 	if (ret)
79 		pr_err("modepin: set value error %d for pin %d\n", ret, pin);
80 
81 	return ret;
82 }
83 
84 /**
85  * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input
86  * @chip:	gpio_chip instance to be worked on
87  * @pin:	gpio pin number within the device
88  *
89  * Return: 0 always
90  */
91 static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
92 {
93 	return 0;
94 }
95 
96 /**
97  * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output
98  * @chip:	gpio_chip instance to be worked on
99  * @pin:	gpio pin number within the device
100  * @state:	value to be written to specified pin
101  *
102  * Return: 0 always
103  */
104 static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
105 				int state)
106 {
107 	return modepin_gpio_set_value(chip, pin, state);
108 }
109 
110 /**
111  * modepin_gpio_probe - Initialization method for modepin_gpio
112  * @pdev:		platform device instance
113  *
114  * Return: 0 on success, negative error otherwise.
115  */
116 static int modepin_gpio_probe(struct platform_device *pdev)
117 {
118 	struct gpio_chip *chip;
119 	int status;
120 
121 	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
122 	if (!chip)
123 		return -ENOMEM;
124 
125 	platform_set_drvdata(pdev, chip);
126 
127 	/* configure the gpio chip */
128 	chip->base = -1;
129 	chip->ngpio = MODE_PINS;
130 	chip->owner = THIS_MODULE;
131 	chip->parent = &pdev->dev;
132 	chip->get = modepin_gpio_get_value;
133 	chip->set = modepin_gpio_set_value;
134 	chip->direction_input = modepin_gpio_dir_in;
135 	chip->direction_output = modepin_gpio_dir_out;
136 	chip->label = dev_name(&pdev->dev);
137 
138 	/* modepin gpio registration */
139 	status = devm_gpiochip_add_data(&pdev->dev, chip, chip);
140 	if (status)
141 		return dev_err_probe(&pdev->dev, status,
142 			      "Failed to add GPIO chip\n");
143 
144 	return status;
145 }
146 
147 static const struct of_device_id modepin_platform_id[] = {
148 	{ .compatible = "xlnx,zynqmp-gpio-modepin", },
149 	{ }
150 };
151 MODULE_DEVICE_TABLE(of, modepin_platform_id);
152 
153 static struct platform_driver modepin_platform_driver = {
154 	.driver = {
155 		.name = "modepin-gpio",
156 		.of_match_table = modepin_platform_id,
157 	},
158 	.probe = modepin_gpio_probe,
159 };
160 
161 module_platform_driver(modepin_platform_driver);
162 
163 MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>");
164 MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration");
165 MODULE_LICENSE("GPL v2");
166