xref: /openbmc/linux/drivers/gpio/gpio-clps711x.c (revision d0b73b48)
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");
200