1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * 74xx MMIO GPIO driver 4 * 5 * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/err.h> 10 #include <linux/gpio/driver.h> 11 #include <linux/gpio/generic.h> 12 #include <linux/mod_devicetable.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/property.h> 16 17 #define MMIO_74XX_DIR_IN BIT(8) 18 #define MMIO_74XX_DIR_OUT BIT(9) 19 #define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) 20 21 struct mmio_74xx_gpio_priv { 22 struct gpio_generic_chip gen_gc; 23 unsigned int flags; 24 }; 25 26 static const struct of_device_id mmio_74xx_gpio_ids[] = { 27 { 28 .compatible = "ti,741g125", 29 .data = (const void *)(MMIO_74XX_DIR_IN | 1), 30 }, 31 { 32 .compatible = "ti,742g125", 33 .data = (const void *)(MMIO_74XX_DIR_IN | 2), 34 }, 35 { 36 .compatible = "ti,74125", 37 .data = (const void *)(MMIO_74XX_DIR_IN | 4), 38 }, 39 { 40 .compatible = "ti,74365", 41 .data = (const void *)(MMIO_74XX_DIR_IN | 6), 42 }, 43 { 44 .compatible = "ti,74244", 45 .data = (const void *)(MMIO_74XX_DIR_IN | 8), 46 }, 47 { 48 .compatible = "ti,741624", 49 .data = (const void *)(MMIO_74XX_DIR_IN | 16), 50 }, 51 { 52 .compatible = "ti,741g74", 53 .data = (const void *)(MMIO_74XX_DIR_OUT | 1), 54 }, 55 { 56 .compatible = "ti,7474", 57 .data = (const void *)(MMIO_74XX_DIR_OUT | 2), 58 }, 59 { 60 .compatible = "ti,74175", 61 .data = (const void *)(MMIO_74XX_DIR_OUT | 4), 62 }, 63 { 64 .compatible = "ti,74174", 65 .data = (const void *)(MMIO_74XX_DIR_OUT | 6), 66 }, 67 { 68 .compatible = "ti,74273", 69 .data = (const void *)(MMIO_74XX_DIR_OUT | 8), 70 }, 71 { 72 .compatible = "ti,7416374", 73 .data = (const void *)(MMIO_74XX_DIR_OUT | 16), 74 }, 75 { } 76 }; 77 MODULE_DEVICE_TABLE(of, mmio_74xx_gpio_ids); 78 79 static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned offset) 80 { 81 struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); 82 83 if (priv->flags & MMIO_74XX_DIR_OUT) 84 return GPIO_LINE_DIRECTION_OUT; 85 86 return GPIO_LINE_DIRECTION_IN; 87 } 88 89 static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) 90 { 91 struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); 92 93 if (priv->flags & MMIO_74XX_DIR_IN) 94 return 0; 95 96 return -ENOTSUPP; 97 } 98 99 static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) 100 { 101 struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); 102 103 if (priv->flags & MMIO_74XX_DIR_OUT) 104 return gpio_generic_chip_set(&priv->gen_gc, gpio, val); 105 106 return -ENOTSUPP; 107 } 108 109 static int mmio_74xx_gpio_probe(struct platform_device *pdev) 110 { 111 struct gpio_generic_chip_config config = { }; 112 struct mmio_74xx_gpio_priv *priv; 113 void __iomem *dat; 114 int err; 115 116 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 117 if (!priv) 118 return -ENOMEM; 119 120 priv->flags = (uintptr_t)device_get_match_data(&pdev->dev); 121 122 dat = devm_platform_ioremap_resource(pdev, 0); 123 if (IS_ERR(dat)) 124 return PTR_ERR(dat); 125 126 config.dev = &pdev->dev; 127 config.sz = DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8); 128 config.dat = dat; 129 130 err = gpio_generic_chip_init(&priv->gen_gc, &config); 131 if (err) 132 return err; 133 134 priv->gen_gc.gc.direction_input = mmio_74xx_dir_in; 135 priv->gen_gc.gc.direction_output = mmio_74xx_dir_out; 136 priv->gen_gc.gc.get_direction = mmio_74xx_get_direction; 137 priv->gen_gc.gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); 138 priv->gen_gc.gc.owner = THIS_MODULE; 139 140 return devm_gpiochip_add_data(&pdev->dev, &priv->gen_gc.gc, priv); 141 } 142 143 static struct platform_driver mmio_74xx_gpio_driver = { 144 .driver = { 145 .name = "74xx-mmio-gpio", 146 .of_match_table = mmio_74xx_gpio_ids, 147 }, 148 .probe = mmio_74xx_gpio_probe, 149 }; 150 module_platform_driver(mmio_74xx_gpio_driver); 151 152 MODULE_LICENSE("GPL"); 153 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 154 MODULE_DESCRIPTION("74xx MMIO GPIO driver"); 155