| // 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(®val); |
| 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", }, |
| { } |
| }; |
| |
| 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"); |