163 lines
4.1 KiB
C
163 lines
4.1 KiB
C
|
// 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");
|