133965Sjdp// SPDX-License-Identifier: GPL-2.0+ 2218822Sdim/* 3218822Sdim * Copyright (C) 2019 Bootlin 433965Sjdp * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 533965Sjdp */ 633965Sjdp 733965Sjdp#include <linux/err.h> 833965Sjdp#include <linux/gpio/driver.h> 933965Sjdp#include <linux/module.h> 1033965Sjdp#include <linux/of.h> 1133965Sjdp#include <linux/of_address.h> 1233965Sjdp#include <linux/platform_device.h> 1333965Sjdp#include <linux/regmap.h> 1433965Sjdp#include <linux/mfd/syscon.h> 1533965Sjdp 1633965Sjdp#define LOGICVC_CTRL_REG 0x40 1733965Sjdp#define LOGICVC_CTRL_GPIO_SHIFT 11 1833965Sjdp#define LOGICVC_CTRL_GPIO_BITS 5 19218822Sdim 20218822Sdim#define LOGICVC_POWER_CTRL_REG 0x78 2133965Sjdp#define LOGICVC_POWER_CTRL_GPIO_SHIFT 0 2233965Sjdp#define LOGICVC_POWER_CTRL_GPIO_BITS 4 23218822Sdim 2433965Sjdpstruct logicvc_gpio { 2533965Sjdp struct gpio_chip chip; 2633965Sjdp struct regmap *regmap; 2733965Sjdp}; 2877298Sobrien 2977298Sobrienstatic void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset, 3033965Sjdp unsigned int *reg, unsigned int *bit) 3133965Sjdp{ 3277298Sobrien if (offset >= LOGICVC_CTRL_GPIO_BITS) { 3333965Sjdp *reg = LOGICVC_POWER_CTRL_REG; 3433965Sjdp 3533965Sjdp /* To the (virtual) power ctrl offset. */ 3633965Sjdp offset -= LOGICVC_CTRL_GPIO_BITS; 3733965Sjdp /* To the actual bit offset in reg. */ 3833965Sjdp offset += LOGICVC_POWER_CTRL_GPIO_SHIFT; 3933965Sjdp } else { 4077298Sobrien *reg = LOGICVC_CTRL_REG; 4133965Sjdp 4233965Sjdp /* To the actual bit offset in reg. */ 4333965Sjdp offset += LOGICVC_CTRL_GPIO_SHIFT; 4433965Sjdp } 4533965Sjdp 4633965Sjdp *bit = BIT(offset); 4733965Sjdp} 4833965Sjdp 4933965Sjdpstatic int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset) 5033965Sjdp{ 5177298Sobrien struct logicvc_gpio *logicvc = gpiochip_get_data(chip); 5277298Sobrien unsigned int reg, bit, value; 5377298Sobrien int ret; 5433965Sjdp 5533965Sjdp logicvc_gpio_offset(logicvc, offset, ®, &bit); 5633965Sjdp 5733965Sjdp ret = regmap_read(logicvc->regmap, reg, &value); 5833965Sjdp if (ret) 59130561Sobrien return ret; 60130561Sobrien 6133965Sjdp return !!(value & bit); 6233965Sjdp} 6333965Sjdp 64130561Sobrienstatic void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 65130561Sobrien{ 66130561Sobrien struct logicvc_gpio *logicvc = gpiochip_get_data(chip); 6777298Sobrien unsigned int reg, bit; 68130561Sobrien 6933965Sjdp logicvc_gpio_offset(logicvc, offset, ®, &bit); 7077298Sobrien 71130561Sobrien regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); 7277298Sobrien} 73130561Sobrien 7433965Sjdpstatic int logicvc_gpio_direction_output(struct gpio_chip *chip, 75130561Sobrien unsigned offset, int value) 7633965Sjdp{ 7777298Sobrien /* Pins are always configured as output, so just set the value. */ 7877298Sobrien logicvc_gpio_set(chip, offset, value); 7933965Sjdp 8033965Sjdp return 0; 8133965Sjdp} 8233965Sjdp 8360484Sobrienstatic struct regmap_config logicvc_gpio_regmap_config = { 8433965Sjdp .reg_bits = 32, 8533965Sjdp .val_bits = 32, 86130561Sobrien .reg_stride = 4, 8733965Sjdp .name = "logicvc-gpio", 88130561Sobrien}; 89130561Sobrien 90130561Sobrienstatic int logicvc_gpio_probe(struct platform_device *pdev) 91130561Sobrien{ 92130561Sobrien struct device *dev = &pdev->dev; 93130561Sobrien struct device_node *of_node = dev->of_node; 94130561Sobrien struct logicvc_gpio *logicvc; 95130561Sobrien int ret; 96130561Sobrien 9733965Sjdp logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL); 9833965Sjdp if (!logicvc) 99130561Sobrien return -ENOMEM; 10033965Sjdp 101130561Sobrien /* Try to get regmap from parent first. */ 102130561Sobrien logicvc->regmap = syscon_node_to_regmap(of_node->parent); 103130561Sobrien 104130561Sobrien /* Grab our own regmap if that fails. */ 105130561Sobrien if (IS_ERR(logicvc->regmap)) { 106218822Sdim struct resource res; 107130561Sobrien void __iomem *base; 10833965Sjdp 109130561Sobrien ret = of_address_to_resource(of_node, 0, &res); 110130561Sobrien if (ret) { 11133965Sjdp dev_err(dev, "Failed to get resource from address\n"); 112130561Sobrien return ret; 113130561Sobrien } 114130561Sobrien 11533965Sjdp base = devm_ioremap_resource(dev, &res); 116130561Sobrien if (IS_ERR(base)) 117130561Sobrien return PTR_ERR(base); 118130561Sobrien 119130561Sobrien logicvc_gpio_regmap_config.max_register = resource_size(&res) - 120130561Sobrien logicvc_gpio_regmap_config.reg_stride; 121130561Sobrien 122130561Sobrien logicvc->regmap = 12377298Sobrien devm_regmap_init_mmio(dev, base, 12433965Sjdp &logicvc_gpio_regmap_config); 12577298Sobrien if (IS_ERR(logicvc->regmap)) { 126130561Sobrien dev_err(dev, "Failed to create regmap for I/O\n"); 12733965Sjdp return PTR_ERR(logicvc->regmap); 12833965Sjdp } 12933965Sjdp } 13033965Sjdp 13133965Sjdp logicvc->chip.parent = dev; 13233965Sjdp logicvc->chip.owner = THIS_MODULE; 13333965Sjdp logicvc->chip.label = dev_name(dev); 13433965Sjdp logicvc->chip.base = -1; 13533965Sjdp logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + 13633965Sjdp LOGICVC_POWER_CTRL_GPIO_BITS; 13777298Sobrien logicvc->chip.get = logicvc_gpio_get; 13877298Sobrien logicvc->chip.set = logicvc_gpio_set; 13933965Sjdp logicvc->chip.direction_output = logicvc_gpio_direction_output; 14033965Sjdp 14133965Sjdp return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); 14233965Sjdp} 14333965Sjdp 14433965Sjdpstatic const struct of_device_id logicivc_gpio_of_table[] = { 14533965Sjdp { 14633965Sjdp .compatible = "xylon,logicvc-3.02.a-gpio", 14733965Sjdp }, 148218822Sdim { } 14933965Sjdp}; 15060484Sobrien 15133965SjdpMODULE_DEVICE_TABLE(of, logicivc_gpio_of_table); 15233965Sjdp 15333965Sjdpstatic struct platform_driver logicvc_gpio_driver = { 15433965Sjdp .driver = { 15533965Sjdp .name = "gpio-logicvc", 15633965Sjdp .of_match_table = logicivc_gpio_of_table, 15733965Sjdp }, 15833965Sjdp .probe = logicvc_gpio_probe, 15933965Sjdp}; 16033965Sjdp 16133965Sjdpmodule_platform_driver(logicvc_gpio_driver); 16233965Sjdp 16338889SjdpMODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 16433965SjdpMODULE_DESCRIPTION("Xylon LogiCVC GPIO driver"); 16533965SjdpMODULE_LICENSE("GPL"); 16633965Sjdp