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