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 */
31static 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 */
60static void 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
82/**
83 * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input
84 * @chip:	gpio_chip instance to be worked on
85 * @pin:	gpio pin number within the device
86 *
87 * Return: 0 always
88 */
89static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
90{
91	return 0;
92}
93
94/**
95 * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output
96 * @chip:	gpio_chip instance to be worked on
97 * @pin:	gpio pin number within the device
98 * @state:	value to be written to specified pin
99 *
100 * Return: 0 always
101 */
102static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
103				int state)
104{
105	return 0;
106}
107
108/**
109 * modepin_gpio_probe - Initialization method for modepin_gpio
110 * @pdev:		platform device instance
111 *
112 * Return: 0 on success, negative error otherwise.
113 */
114static int modepin_gpio_probe(struct platform_device *pdev)
115{
116	struct gpio_chip *chip;
117	int status;
118
119	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
120	if (!chip)
121		return -ENOMEM;
122
123	platform_set_drvdata(pdev, chip);
124
125	/* configure the gpio chip */
126	chip->base = -1;
127	chip->ngpio = MODE_PINS;
128	chip->owner = THIS_MODULE;
129	chip->parent = &pdev->dev;
130	chip->get = modepin_gpio_get_value;
131	chip->set = modepin_gpio_set_value;
132	chip->direction_input = modepin_gpio_dir_in;
133	chip->direction_output = modepin_gpio_dir_out;
134	chip->label = dev_name(&pdev->dev);
135
136	/* modepin gpio registration */
137	status = devm_gpiochip_add_data(&pdev->dev, chip, chip);
138	if (status)
139		return dev_err_probe(&pdev->dev, status,
140			      "Failed to add GPIO chip\n");
141
142	return status;
143}
144
145static const struct of_device_id modepin_platform_id[] = {
146	{ .compatible = "xlnx,zynqmp-gpio-modepin", },
147	{ }
148};
149
150static struct platform_driver modepin_platform_driver = {
151	.driver = {
152		.name = "modepin-gpio",
153		.of_match_table = modepin_platform_id,
154	},
155	.probe = modepin_gpio_probe,
156};
157
158module_platform_driver(modepin_platform_driver);
159
160MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>");
161MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration");
162MODULE_LICENSE("GPL v2");
163