18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * MEN 16Z127 GPIO driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 MEN Mikroelektronik GmbH (www.men.de)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/mcb.h>
138c2ecf20Sopenharmony_ci#include <linux/bitops.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define MEN_Z127_CTRL	0x00
178c2ecf20Sopenharmony_ci#define MEN_Z127_PSR	0x04
188c2ecf20Sopenharmony_ci#define MEN_Z127_IRQR	0x08
198c2ecf20Sopenharmony_ci#define MEN_Z127_GPIODR	0x0c
208c2ecf20Sopenharmony_ci#define MEN_Z127_IER1	0x10
218c2ecf20Sopenharmony_ci#define MEN_Z127_IER2	0x14
228c2ecf20Sopenharmony_ci#define MEN_Z127_DBER	0x18
238c2ecf20Sopenharmony_ci#define MEN_Z127_ODER	0x1C
248c2ecf20Sopenharmony_ci#define GPIO_TO_DBCNT_REG(gpio)	((gpio * 4) + 0x80)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define MEN_Z127_DB_MIN_US	50
278c2ecf20Sopenharmony_ci/* 16 bit compare register. Each bit represents 50us */
288c2ecf20Sopenharmony_ci#define MEN_Z127_DB_MAX_US	(0xffff * MEN_Z127_DB_MIN_US)
298c2ecf20Sopenharmony_ci#define MEN_Z127_DB_IN_RANGE(db)	((db >= MEN_Z127_DB_MIN_US) && \
308c2ecf20Sopenharmony_ci					 (db <= MEN_Z127_DB_MAX_US))
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct men_z127_gpio {
338c2ecf20Sopenharmony_ci	struct gpio_chip gc;
348c2ecf20Sopenharmony_ci	void __iomem *reg_base;
358c2ecf20Sopenharmony_ci	struct resource *mem;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int men_z127_debounce(struct gpio_chip *gc, unsigned gpio,
398c2ecf20Sopenharmony_ci			     unsigned debounce)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct men_z127_gpio *priv = gpiochip_get_data(gc);
428c2ecf20Sopenharmony_ci	struct device *dev = gc->parent;
438c2ecf20Sopenharmony_ci	unsigned int rnd;
448c2ecf20Sopenharmony_ci	u32 db_en, db_cnt;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!MEN_Z127_DB_IN_RANGE(debounce)) {
478c2ecf20Sopenharmony_ci		dev_err(dev, "debounce value %u out of range", debounce);
488c2ecf20Sopenharmony_ci		return -EINVAL;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (debounce > 0) {
528c2ecf20Sopenharmony_ci		/* round up or down depending on MSB-1 */
538c2ecf20Sopenharmony_ci		rnd = fls(debounce) - 1;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		if (rnd && (debounce & BIT(rnd - 1)))
568c2ecf20Sopenharmony_ci			debounce = roundup(debounce, MEN_Z127_DB_MIN_US);
578c2ecf20Sopenharmony_ci		else
588c2ecf20Sopenharmony_ci			debounce = rounddown(debounce, MEN_Z127_DB_MIN_US);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		if (debounce > MEN_Z127_DB_MAX_US)
618c2ecf20Sopenharmony_ci			debounce = MEN_Z127_DB_MAX_US;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		/* 50us per register unit */
648c2ecf20Sopenharmony_ci		debounce /= 50;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	spin_lock(&gc->bgpio_lock);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	db_en = readl(priv->reg_base + MEN_Z127_DBER);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (debounce == 0) {
728c2ecf20Sopenharmony_ci		db_en &= ~BIT(gpio);
738c2ecf20Sopenharmony_ci		db_cnt = 0;
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		db_en |= BIT(gpio);
768c2ecf20Sopenharmony_ci		db_cnt = debounce;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	writel(db_en, priv->reg_base + MEN_Z127_DBER);
808c2ecf20Sopenharmony_ci	writel(db_cnt, priv->reg_base + GPIO_TO_DBCNT_REG(gpio));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	spin_unlock(&gc->bgpio_lock);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int men_z127_set_single_ended(struct gpio_chip *gc,
888c2ecf20Sopenharmony_ci				     unsigned offset,
898c2ecf20Sopenharmony_ci				     enum pin_config_param param)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct men_z127_gpio *priv = gpiochip_get_data(gc);
928c2ecf20Sopenharmony_ci	u32 od_en;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	spin_lock(&gc->bgpio_lock);
958c2ecf20Sopenharmony_ci	od_en = readl(priv->reg_base + MEN_Z127_ODER);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
988c2ecf20Sopenharmony_ci		od_en |= BIT(offset);
998c2ecf20Sopenharmony_ci	else
1008c2ecf20Sopenharmony_ci		/* Implicitly PIN_CONFIG_DRIVE_PUSH_PULL */
1018c2ecf20Sopenharmony_ci		od_en &= ~BIT(offset);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	writel(od_en, priv->reg_base + MEN_Z127_ODER);
1048c2ecf20Sopenharmony_ci	spin_unlock(&gc->bgpio_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int men_z127_set_config(struct gpio_chip *gc, unsigned offset,
1108c2ecf20Sopenharmony_ci			       unsigned long config)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	enum pin_config_param param = pinconf_to_config_param(config);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	switch (param) {
1158c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
1168c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_PUSH_PULL:
1178c2ecf20Sopenharmony_ci		return men_z127_set_single_ended(gc, offset, param);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	case PIN_CONFIG_INPUT_DEBOUNCE:
1208c2ecf20Sopenharmony_ci		return men_z127_debounce(gc, offset,
1218c2ecf20Sopenharmony_ci			pinconf_to_config_argument(config));
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	default:
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return -ENOTSUPP;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int men_z127_probe(struct mcb_device *mdev,
1318c2ecf20Sopenharmony_ci			  const struct mcb_device_id *id)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct men_z127_gpio *men_z127_gpio;
1348c2ecf20Sopenharmony_ci	struct device *dev = &mdev->dev;
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio),
1388c2ecf20Sopenharmony_ci				     GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	if (!men_z127_gpio)
1408c2ecf20Sopenharmony_ci		return -ENOMEM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	men_z127_gpio->mem = mcb_request_mem(mdev, dev_name(dev));
1438c2ecf20Sopenharmony_ci	if (IS_ERR(men_z127_gpio->mem)) {
1448c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request device memory");
1458c2ecf20Sopenharmony_ci		return PTR_ERR(men_z127_gpio->mem);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	men_z127_gpio->reg_base = ioremap(men_z127_gpio->mem->start,
1498c2ecf20Sopenharmony_ci					  resource_size(men_z127_gpio->mem));
1508c2ecf20Sopenharmony_ci	if (men_z127_gpio->reg_base == NULL) {
1518c2ecf20Sopenharmony_ci		ret = -ENXIO;
1528c2ecf20Sopenharmony_ci		goto err_release;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	mcb_set_drvdata(mdev, men_z127_gpio);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = bgpio_init(&men_z127_gpio->gc, &mdev->dev, 4,
1588c2ecf20Sopenharmony_ci			 men_z127_gpio->reg_base + MEN_Z127_PSR,
1598c2ecf20Sopenharmony_ci			 men_z127_gpio->reg_base + MEN_Z127_CTRL,
1608c2ecf20Sopenharmony_ci			 NULL,
1618c2ecf20Sopenharmony_ci			 men_z127_gpio->reg_base + MEN_Z127_GPIODR,
1628c2ecf20Sopenharmony_ci			 NULL, 0);
1638c2ecf20Sopenharmony_ci	if (ret)
1648c2ecf20Sopenharmony_ci		goto err_unmap;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	men_z127_gpio->gc.set_config = men_z127_set_config;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ret = gpiochip_add_data(&men_z127_gpio->gc, men_z127_gpio);
1698c2ecf20Sopenharmony_ci	if (ret) {
1708c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register MEN 16Z127 GPIO controller");
1718c2ecf20Sopenharmony_ci		goto err_unmap;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	dev_info(dev, "MEN 16Z127 GPIO driver registered");
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cierr_unmap:
1798c2ecf20Sopenharmony_ci	iounmap(men_z127_gpio->reg_base);
1808c2ecf20Sopenharmony_cierr_release:
1818c2ecf20Sopenharmony_ci	mcb_release_mem(men_z127_gpio->mem);
1828c2ecf20Sopenharmony_ci	return ret;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void men_z127_remove(struct mcb_device *mdev)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct men_z127_gpio *men_z127_gpio = mcb_get_drvdata(mdev);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	gpiochip_remove(&men_z127_gpio->gc);
1908c2ecf20Sopenharmony_ci	iounmap(men_z127_gpio->reg_base);
1918c2ecf20Sopenharmony_ci	mcb_release_mem(men_z127_gpio->mem);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic const struct mcb_device_id men_z127_ids[] = {
1958c2ecf20Sopenharmony_ci	{ .device = 0x7f },
1968c2ecf20Sopenharmony_ci	{ }
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mcb, men_z127_ids);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic struct mcb_driver men_z127_driver = {
2018c2ecf20Sopenharmony_ci	.driver = {
2028c2ecf20Sopenharmony_ci		.name = "z127-gpio",
2038c2ecf20Sopenharmony_ci	},
2048c2ecf20Sopenharmony_ci	.probe = men_z127_probe,
2058c2ecf20Sopenharmony_ci	.remove = men_z127_remove,
2068c2ecf20Sopenharmony_ci	.id_table = men_z127_ids,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_cimodule_mcb_driver(men_z127_driver);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
2118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MEN 16z127 GPIO Controller");
2128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2138c2ecf20Sopenharmony_ciMODULE_ALIAS("mcb:16z127");
2148c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(MCB);
215