162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the ps-mode pin configuration. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2021 Xilinx, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/firmware/xlnx-zynqmp.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 4-bit boot mode pins */ 1962306a36Sopenharmony_ci#define MODE_PINS 4 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * modepin_gpio_get_value - Get the state of the specified pin of GPIO device 2362306a36Sopenharmony_ci * @chip: gpio_chip instance to be worked on 2462306a36Sopenharmony_ci * @pin: gpio pin number within the device 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * This function reads the state of the specified pin of the GPIO device. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured 2962306a36Sopenharmony_ci * or error value. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci u32 regval = 0; 3462306a36Sopenharmony_ci int ret; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci ret = zynqmp_pm_bootmode_read(®val); 3762306a36Sopenharmony_ci if (ret) 3862306a36Sopenharmony_ci return ret; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* When [0:3] corresponding bit is set, then read output bit [8:11], 4162306a36Sopenharmony_ci * if the bit is clear then read input bit [4:7] for status or value. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci if (regval & BIT(pin)) 4462306a36Sopenharmony_ci return !!(regval & BIT(pin + 8)); 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci return !!(regval & BIT(pin + 4)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * modepin_gpio_set_value - Modify the state of the pin with specified value 5162306a36Sopenharmony_ci * @chip: gpio_chip instance to be worked on 5262306a36Sopenharmony_ci * @pin: gpio pin number within the device 5362306a36Sopenharmony_ci * @state: value used to modify the state of the specified pin 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * This function reads the state of the specified pin of the GPIO device, mask 5662306a36Sopenharmony_ci * with the capture state of GPIO pin, and update pin of GPIO device. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Return: None. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, 6162306a36Sopenharmony_ci int state) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 bootpin_val = 0; 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci zynqmp_pm_bootmode_read(&bootpin_val); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Configure pin as an output by set bit [0:3] */ 6962306a36Sopenharmony_ci bootpin_val |= BIT(pin); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (state) 7262306a36Sopenharmony_ci bootpin_val |= BIT(pin + 8); 7362306a36Sopenharmony_ci else 7462306a36Sopenharmony_ci bootpin_val &= ~BIT(pin + 8); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Configure bootpin value */ 7762306a36Sopenharmony_ci ret = zynqmp_pm_bootmode_write(bootpin_val); 7862306a36Sopenharmony_ci if (ret) 7962306a36Sopenharmony_ci pr_err("modepin: set value error %d for pin %d\n", ret, pin); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input 8462306a36Sopenharmony_ci * @chip: gpio_chip instance to be worked on 8562306a36Sopenharmony_ci * @pin: gpio pin number within the device 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Return: 0 always 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output 9662306a36Sopenharmony_ci * @chip: gpio_chip instance to be worked on 9762306a36Sopenharmony_ci * @pin: gpio pin number within the device 9862306a36Sopenharmony_ci * @state: value to be written to specified pin 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Return: 0 always 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, 10362306a36Sopenharmony_ci int state) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * modepin_gpio_probe - Initialization method for modepin_gpio 11062306a36Sopenharmony_ci * @pdev: platform device instance 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Return: 0 on success, negative error otherwise. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_cistatic int modepin_gpio_probe(struct platform_device *pdev) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct gpio_chip *chip; 11762306a36Sopenharmony_ci int status; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 12062306a36Sopenharmony_ci if (!chip) 12162306a36Sopenharmony_ci return -ENOMEM; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci platform_set_drvdata(pdev, chip); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* configure the gpio chip */ 12662306a36Sopenharmony_ci chip->base = -1; 12762306a36Sopenharmony_ci chip->ngpio = MODE_PINS; 12862306a36Sopenharmony_ci chip->owner = THIS_MODULE; 12962306a36Sopenharmony_ci chip->parent = &pdev->dev; 13062306a36Sopenharmony_ci chip->get = modepin_gpio_get_value; 13162306a36Sopenharmony_ci chip->set = modepin_gpio_set_value; 13262306a36Sopenharmony_ci chip->direction_input = modepin_gpio_dir_in; 13362306a36Sopenharmony_ci chip->direction_output = modepin_gpio_dir_out; 13462306a36Sopenharmony_ci chip->label = dev_name(&pdev->dev); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* modepin gpio registration */ 13762306a36Sopenharmony_ci status = devm_gpiochip_add_data(&pdev->dev, chip, chip); 13862306a36Sopenharmony_ci if (status) 13962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, status, 14062306a36Sopenharmony_ci "Failed to add GPIO chip\n"); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return status; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct of_device_id modepin_platform_id[] = { 14662306a36Sopenharmony_ci { .compatible = "xlnx,zynqmp-gpio-modepin", }, 14762306a36Sopenharmony_ci { } 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct platform_driver modepin_platform_driver = { 15162306a36Sopenharmony_ci .driver = { 15262306a36Sopenharmony_ci .name = "modepin-gpio", 15362306a36Sopenharmony_ci .of_match_table = modepin_platform_id, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci .probe = modepin_gpio_probe, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cimodule_platform_driver(modepin_platform_driver); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciMODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>"); 16162306a36Sopenharmony_ciMODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration"); 16262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 163