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(ps_mode: ®val); |
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 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(ps_mode: &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(ps_mode: 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 | */ |
89 | static 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 | */ |
102 | static 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 | */ |
114 | static int modepin_gpio_probe(struct platform_device *pdev) |
115 | { |
116 | struct gpio_chip *chip; |
117 | int status; |
118 | |
119 | chip = devm_kzalloc(dev: &pdev->dev, size: sizeof(*chip), GFP_KERNEL); |
120 | if (!chip) |
121 | return -ENOMEM; |
122 | |
123 | platform_set_drvdata(pdev, data: 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(dev: &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(dev: &pdev->dev, err: status, |
140 | fmt: "Failed to add GPIO chip\n" ); |
141 | |
142 | return status; |
143 | } |
144 | |
145 | static const struct of_device_id modepin_platform_id[] = { |
146 | { .compatible = "xlnx,zynqmp-gpio-modepin" , }, |
147 | { } |
148 | }; |
149 | |
150 | static 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 | |
158 | module_platform_driver(modepin_platform_driver); |
159 | |
160 | MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>" ); |
161 | MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration" ); |
162 | MODULE_LICENSE("GPL v2" ); |
163 | |