162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright Intel Corporation (C) 2014-2016. All Rights Reserved
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * GPIO driver for  Altera Arria10 MAX5 System Resource Chip
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Adapted from gpio-tps65910.c
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1162306a36Sopenharmony_ci#include <linux/mfd/altera-a10sr.h>
1262306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/property.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/**
1762306a36Sopenharmony_ci * struct altr_a10sr_gpio - Altera Max5 GPIO device private data structure
1862306a36Sopenharmony_ci * @gp:   : instance of the gpio_chip
1962306a36Sopenharmony_ci * @regmap: the regmap from the parent device.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistruct altr_a10sr_gpio {
2262306a36Sopenharmony_ci	struct gpio_chip gp;
2362306a36Sopenharmony_ci	struct regmap *regmap;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int altr_a10sr_gpio_get(struct gpio_chip *chip, unsigned int offset)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct altr_a10sr_gpio *gpio = gpiochip_get_data(chip);
2962306a36Sopenharmony_ci	int ret, val;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	ret = regmap_read(gpio->regmap, ALTR_A10SR_PBDSW_REG, &val);
3262306a36Sopenharmony_ci	if (ret < 0)
3362306a36Sopenharmony_ci		return ret;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return !!(val & BIT(offset - ALTR_A10SR_LED_VALID_SHIFT));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset,
3962306a36Sopenharmony_ci				int value)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct altr_a10sr_gpio *gpio = gpiochip_get_data(chip);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	regmap_update_bits(gpio->regmap, ALTR_A10SR_LED_REG,
4462306a36Sopenharmony_ci			   BIT(ALTR_A10SR_LED_VALID_SHIFT + offset),
4562306a36Sopenharmony_ci			   value ? BIT(ALTR_A10SR_LED_VALID_SHIFT + offset)
4662306a36Sopenharmony_ci			   : 0);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int altr_a10sr_gpio_direction_input(struct gpio_chip *gc,
5062306a36Sopenharmony_ci					   unsigned int nr)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT))
5362306a36Sopenharmony_ci		return -EINVAL;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int altr_a10sr_gpio_direction_output(struct gpio_chip *gc,
5962306a36Sopenharmony_ci					    unsigned int nr, int value)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT))
6262306a36Sopenharmony_ci		return -EINVAL;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	altr_a10sr_gpio_set(gc, nr, value);
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic const struct gpio_chip altr_a10sr_gc = {
6962306a36Sopenharmony_ci	.label = "altr_a10sr_gpio",
7062306a36Sopenharmony_ci	.owner = THIS_MODULE,
7162306a36Sopenharmony_ci	.get = altr_a10sr_gpio_get,
7262306a36Sopenharmony_ci	.set = altr_a10sr_gpio_set,
7362306a36Sopenharmony_ci	.direction_input = altr_a10sr_gpio_direction_input,
7462306a36Sopenharmony_ci	.direction_output = altr_a10sr_gpio_direction_output,
7562306a36Sopenharmony_ci	.can_sleep = true,
7662306a36Sopenharmony_ci	.ngpio = 12,
7762306a36Sopenharmony_ci	.base = -1,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int altr_a10sr_gpio_probe(struct platform_device *pdev)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct altr_a10sr_gpio *gpio;
8362306a36Sopenharmony_ci	struct altr_a10sr *a10sr = dev_get_drvdata(pdev->dev.parent);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
8662306a36Sopenharmony_ci	if (!gpio)
8762306a36Sopenharmony_ci		return -ENOMEM;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	gpio->regmap = a10sr->regmap;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	gpio->gp = altr_a10sr_gc;
9262306a36Sopenharmony_ci	gpio->gp.parent = pdev->dev.parent;
9362306a36Sopenharmony_ci	gpio->gp.fwnode = dev_fwnode(&pdev->dev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct of_device_id altr_a10sr_gpio_of_match[] = {
9962306a36Sopenharmony_ci	{ .compatible = "altr,a10sr-gpio" },
10062306a36Sopenharmony_ci	{ },
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_a10sr_gpio_of_match);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct platform_driver altr_a10sr_gpio_driver = {
10562306a36Sopenharmony_ci	.probe = altr_a10sr_gpio_probe,
10662306a36Sopenharmony_ci	.driver = {
10762306a36Sopenharmony_ci		.name	= "altr_a10sr_gpio",
10862306a36Sopenharmony_ci		.of_match_table = altr_a10sr_gpio_of_match,
10962306a36Sopenharmony_ci	},
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_cimodule_platform_driver(altr_a10sr_gpio_driver);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
11462306a36Sopenharmony_ciMODULE_AUTHOR("Thor Thayer <tthayer@opensource.altera.com>");
11562306a36Sopenharmony_ciMODULE_DESCRIPTION("Altera Arria10 System Resource Chip GPIO");
116