1/* 2 * linux/drivers/gpio/gpio-mb86s7x.c 3 * 4 * Copyright (C) 2015 Fujitsu Semiconductor Limited 5 * Copyright (C) 2015 Linaro Ltd. 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, version 2 of the License. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17#include <linux/io.h> 18#include <linux/init.h> 19#include <linux/clk.h> 20#include <linux/module.h> 21#include <linux/err.h> 22#include <linux/errno.h> 23#include <linux/ioport.h> 24#include <linux/of_device.h> 25#include <linux/gpio/driver.h> 26#include <linux/platform_device.h> 27#include <linux/spinlock.h> 28#include <linux/slab.h> 29 30/* 31 * Only first 8bits of a register correspond to each pin, 32 * so there are 4 registers for 32 pins. 33 */ 34#define PDR(x) (0x0 + x / 8 * 4) 35#define DDR(x) (0x10 + x / 8 * 4) 36#define PFR(x) (0x20 + x / 8 * 4) 37 38#define OFFSET(x) BIT((x) % 8) 39 40struct mb86s70_gpio_chip { 41 struct gpio_chip gc; 42 void __iomem *base; 43 struct clk *clk; 44 spinlock_t lock; 45}; 46 47static inline struct mb86s70_gpio_chip *chip_to_mb86s70(struct gpio_chip *gc) 48{ 49 return container_of(gc, struct mb86s70_gpio_chip, gc); 50} 51 52static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned gpio) 53{ 54 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 55 unsigned long flags; 56 u32 val; 57 58 spin_lock_irqsave(&gchip->lock, flags); 59 60 val = readl(gchip->base + PFR(gpio)); 61 if (!(val & OFFSET(gpio))) { 62 spin_unlock_irqrestore(&gchip->lock, flags); 63 return -EINVAL; 64 } 65 66 val &= ~OFFSET(gpio); 67 writel(val, gchip->base + PFR(gpio)); 68 69 spin_unlock_irqrestore(&gchip->lock, flags); 70 71 return 0; 72} 73 74static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned gpio) 75{ 76 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 77 unsigned long flags; 78 u32 val; 79 80 spin_lock_irqsave(&gchip->lock, flags); 81 82 val = readl(gchip->base + PFR(gpio)); 83 val |= OFFSET(gpio); 84 writel(val, gchip->base + PFR(gpio)); 85 86 spin_unlock_irqrestore(&gchip->lock, flags); 87} 88 89static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) 90{ 91 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 92 unsigned long flags; 93 unsigned char val; 94 95 spin_lock_irqsave(&gchip->lock, flags); 96 97 val = readl(gchip->base + DDR(gpio)); 98 val &= ~OFFSET(gpio); 99 writel(val, gchip->base + DDR(gpio)); 100 101 spin_unlock_irqrestore(&gchip->lock, flags); 102 103 return 0; 104} 105 106static int mb86s70_gpio_direction_output(struct gpio_chip *gc, 107 unsigned gpio, int value) 108{ 109 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 110 unsigned long flags; 111 unsigned char val; 112 113 spin_lock_irqsave(&gchip->lock, flags); 114 115 val = readl(gchip->base + PDR(gpio)); 116 if (value) 117 val |= OFFSET(gpio); 118 else 119 val &= ~OFFSET(gpio); 120 writel(val, gchip->base + PDR(gpio)); 121 122 val = readl(gchip->base + DDR(gpio)); 123 val |= OFFSET(gpio); 124 writel(val, gchip->base + DDR(gpio)); 125 126 spin_unlock_irqrestore(&gchip->lock, flags); 127 128 return 0; 129} 130 131static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned gpio) 132{ 133 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 134 135 return !!(readl(gchip->base + PDR(gpio)) & OFFSET(gpio)); 136} 137 138static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) 139{ 140 struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc); 141 unsigned long flags; 142 unsigned char val; 143 144 spin_lock_irqsave(&gchip->lock, flags); 145 146 val = readl(gchip->base + PDR(gpio)); 147 if (value) 148 val |= OFFSET(gpio); 149 else 150 val &= ~OFFSET(gpio); 151 writel(val, gchip->base + PDR(gpio)); 152 153 spin_unlock_irqrestore(&gchip->lock, flags); 154} 155 156static int mb86s70_gpio_probe(struct platform_device *pdev) 157{ 158 struct mb86s70_gpio_chip *gchip; 159 struct resource *res; 160 int ret; 161 162 gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL); 163 if (gchip == NULL) 164 return -ENOMEM; 165 166 platform_set_drvdata(pdev, gchip); 167 168 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 169 gchip->base = devm_ioremap_resource(&pdev->dev, res); 170 if (IS_ERR(gchip->base)) 171 return PTR_ERR(gchip->base); 172 173 gchip->clk = devm_clk_get(&pdev->dev, NULL); 174 if (IS_ERR(gchip->clk)) 175 return PTR_ERR(gchip->clk); 176 177 clk_prepare_enable(gchip->clk); 178 179 spin_lock_init(&gchip->lock); 180 181 gchip->gc.direction_output = mb86s70_gpio_direction_output; 182 gchip->gc.direction_input = mb86s70_gpio_direction_input; 183 gchip->gc.request = mb86s70_gpio_request; 184 gchip->gc.free = mb86s70_gpio_free; 185 gchip->gc.get = mb86s70_gpio_get; 186 gchip->gc.set = mb86s70_gpio_set; 187 gchip->gc.label = dev_name(&pdev->dev); 188 gchip->gc.ngpio = 32; 189 gchip->gc.owner = THIS_MODULE; 190 gchip->gc.dev = &pdev->dev; 191 gchip->gc.base = -1; 192 193 platform_set_drvdata(pdev, gchip); 194 195 ret = gpiochip_add(&gchip->gc); 196 if (ret) { 197 dev_err(&pdev->dev, "couldn't register gpio driver\n"); 198 clk_disable_unprepare(gchip->clk); 199 } 200 201 return ret; 202} 203 204static int mb86s70_gpio_remove(struct platform_device *pdev) 205{ 206 struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev); 207 208 gpiochip_remove(&gchip->gc); 209 clk_disable_unprepare(gchip->clk); 210 211 return 0; 212} 213 214static const struct of_device_id mb86s70_gpio_dt_ids[] = { 215 { .compatible = "fujitsu,mb86s70-gpio" }, 216 { /* sentinel */ } 217}; 218MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids); 219 220static struct platform_driver mb86s70_gpio_driver = { 221 .driver = { 222 .name = "mb86s70-gpio", 223 .of_match_table = mb86s70_gpio_dt_ids, 224 }, 225 .probe = mb86s70_gpio_probe, 226 .remove = mb86s70_gpio_remove, 227}; 228 229static int __init mb86s70_gpio_init(void) 230{ 231 return platform_driver_register(&mb86s70_gpio_driver); 232} 233module_init(mb86s70_gpio_init); 234 235MODULE_DESCRIPTION("MB86S7x GPIO Driver"); 236MODULE_ALIAS("platform:mb86s70-gpio"); 237MODULE_LICENSE("GPL"); 238