Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[firefly-linux-kernel-4.4.55.git] / drivers / gpio / gpio-clps711x.c
1 /*
2  *  CLPS711X GPIO driver
3  *
4  *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <linux/io.h>
13 #include <linux/slab.h>
14 #include <linux/gpio.h>
15 #include <linux/module.h>
16 #include <linux/spinlock.h>
17 #include <linux/platform_device.h>
18
19 #include <mach/hardware.h>
20
21 #define CLPS711X_GPIO_PORTS     5
22 #define CLPS711X_GPIO_NAME      "gpio-clps711x"
23
24 struct clps711x_gpio {
25         struct gpio_chip        chip[CLPS711X_GPIO_PORTS];
26         spinlock_t              lock;
27 };
28
29 static void __iomem *clps711x_ports[] = {
30         CLPS711X_VIRT_BASE + PADR,
31         CLPS711X_VIRT_BASE + PBDR,
32         CLPS711X_VIRT_BASE + PCDR,
33         CLPS711X_VIRT_BASE + PDDR,
34         CLPS711X_VIRT_BASE + PEDR,
35 };
36
37 static void __iomem *clps711x_pdirs[] = {
38         CLPS711X_VIRT_BASE + PADDR,
39         CLPS711X_VIRT_BASE + PBDDR,
40         CLPS711X_VIRT_BASE + PCDDR,
41         CLPS711X_VIRT_BASE + PDDDR,
42         CLPS711X_VIRT_BASE + PEDDR,
43 };
44
45 #define clps711x_port(x)        clps711x_ports[x->base / 8]
46 #define clps711x_pdir(x)        clps711x_pdirs[x->base / 8]
47
48 static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset)
49 {
50         return !!(readb(clps711x_port(chip)) & (1 << offset));
51 }
52
53 static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset,
54                               int value)
55 {
56         int tmp;
57         unsigned long flags;
58         struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
59
60         spin_lock_irqsave(&gpio->lock, flags);
61         tmp = readb(clps711x_port(chip)) & ~(1 << offset);
62         if (value)
63                 tmp |= 1 << offset;
64         writeb(tmp, clps711x_port(chip));
65         spin_unlock_irqrestore(&gpio->lock, flags);
66 }
67
68 static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset)
69 {
70         int tmp;
71         unsigned long flags;
72         struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
73
74         spin_lock_irqsave(&gpio->lock, flags);
75         tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
76         writeb(tmp, clps711x_pdir(chip));
77         spin_unlock_irqrestore(&gpio->lock, flags);
78
79         return 0;
80 }
81
82 static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset,
83                                  int value)
84 {
85         int tmp;
86         unsigned long flags;
87         struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
88
89         spin_lock_irqsave(&gpio->lock, flags);
90         tmp = readb(clps711x_pdir(chip)) | (1 << offset);
91         writeb(tmp, clps711x_pdir(chip));
92         tmp = readb(clps711x_port(chip)) & ~(1 << offset);
93         if (value)
94                 tmp |= 1 << offset;
95         writeb(tmp, clps711x_port(chip));
96         spin_unlock_irqrestore(&gpio->lock, flags);
97
98         return 0;
99 }
100
101 static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset)
102 {
103         int tmp;
104         unsigned long flags;
105         struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
106
107         spin_lock_irqsave(&gpio->lock, flags);
108         tmp = readb(clps711x_pdir(chip)) | (1 << offset);
109         writeb(tmp, clps711x_pdir(chip));
110         spin_unlock_irqrestore(&gpio->lock, flags);
111
112         return 0;
113 }
114
115 static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset,
116                                      int value)
117 {
118         int tmp;
119         unsigned long flags;
120         struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
121
122         spin_lock_irqsave(&gpio->lock, flags);
123         tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
124         writeb(tmp, clps711x_pdir(chip));
125         tmp = readb(clps711x_port(chip)) & ~(1 << offset);
126         if (value)
127                 tmp |= 1 << offset;
128         writeb(tmp, clps711x_port(chip));
129         spin_unlock_irqrestore(&gpio->lock, flags);
130
131         return 0;
132 }
133
134 static struct {
135         char    *name;
136         int     nr;
137         int     inv_dir;
138 } clps711x_gpio_ports[] __initconst = {
139         { "PORTA", 8, 0, },
140         { "PORTB", 8, 0, },
141         { "PORTC", 8, 0, },
142         { "PORTD", 8, 1, },
143         { "PORTE", 3, 0, },
144 };
145
146 static int __init gpio_clps711x_init(void)
147 {
148         int i;
149         struct platform_device *pdev;
150         struct clps711x_gpio *gpio;
151
152         pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0);
153         if (!pdev) {
154                 pr_err("Cannot create platform device: %s\n",
155                        CLPS711X_GPIO_NAME);
156                 return -ENOMEM;
157         }
158
159         platform_device_add(pdev);
160
161         gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio),
162                             GFP_KERNEL);
163         if (!gpio) {
164                 dev_err(&pdev->dev, "GPIO allocating memory error\n");
165                 platform_device_unregister(pdev);
166                 return -ENOMEM;
167         }
168
169         platform_set_drvdata(pdev, gpio);
170
171         spin_lock_init(&gpio->lock);
172
173         for (i = 0; i < CLPS711X_GPIO_PORTS; i++) {
174                 gpio->chip[i].owner             = THIS_MODULE;
175                 gpio->chip[i].dev               = &pdev->dev;
176                 gpio->chip[i].label             = clps711x_gpio_ports[i].name;
177                 gpio->chip[i].base              = i * 8;
178                 gpio->chip[i].ngpio             = clps711x_gpio_ports[i].nr;
179                 gpio->chip[i].get               = gpio_clps711x_get;
180                 gpio->chip[i].set               = gpio_clps711x_set;
181                 if (!clps711x_gpio_ports[i].inv_dir) {
182                         gpio->chip[i].direction_input = gpio_clps711x_dir_in;
183                         gpio->chip[i].direction_output = gpio_clps711x_dir_out;
184                 } else {
185                         gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv;
186                         gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv;
187                 }
188                 WARN_ON(gpiochip_add(&gpio->chip[i]));
189         }
190
191         dev_info(&pdev->dev, "GPIO driver initialized\n");
192
193         return 0;
194 }
195 arch_initcall(gpio_clps711x_init);
196
197 MODULE_LICENSE("GPL v2");
198 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
199 MODULE_DESCRIPTION("CLPS711X GPIO driver");