Root/
1 | /* |
2 | * MC33880 high-side/low-side switch GPIO driver |
3 | * Copyright (c) 2009 Intel Corporation |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
17 | */ |
18 | |
19 | /* Supports: |
20 | * Freescale MC33880 high-side/low-side switch |
21 | */ |
22 | |
23 | #include <linux/init.h> |
24 | #include <linux/mutex.h> |
25 | #include <linux/spi/spi.h> |
26 | #include <linux/spi/mc33880.h> |
27 | #include <linux/gpio.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/module.h> |
30 | |
31 | #define DRIVER_NAME "mc33880" |
32 | |
33 | /* |
34 | * Pin configurations, see MAX7301 datasheet page 6 |
35 | */ |
36 | #define PIN_CONFIG_MASK 0x03 |
37 | #define PIN_CONFIG_IN_PULLUP 0x03 |
38 | #define PIN_CONFIG_IN_WO_PULLUP 0x02 |
39 | #define PIN_CONFIG_OUT 0x01 |
40 | |
41 | #define PIN_NUMBER 8 |
42 | |
43 | |
44 | /* |
45 | * Some registers must be read back to modify. |
46 | * To save time we cache them here in memory |
47 | */ |
48 | struct mc33880 { |
49 | struct mutex lock; /* protect from simultaneous accesses */ |
50 | u8 port_config; |
51 | struct gpio_chip chip; |
52 | struct spi_device *spi; |
53 | }; |
54 | |
55 | static int mc33880_write_config(struct mc33880 *mc) |
56 | { |
57 | return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); |
58 | } |
59 | |
60 | |
61 | static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) |
62 | { |
63 | if (value) |
64 | mc->port_config |= 1 << offset; |
65 | else |
66 | mc->port_config &= ~(1 << offset); |
67 | |
68 | return mc33880_write_config(mc); |
69 | } |
70 | |
71 | |
72 | static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) |
73 | { |
74 | struct mc33880 *mc = container_of(chip, struct mc33880, chip); |
75 | |
76 | mutex_lock(&mc->lock); |
77 | |
78 | __mc33880_set(mc, offset, value); |
79 | |
80 | mutex_unlock(&mc->lock); |
81 | } |
82 | |
83 | static int __devinit mc33880_probe(struct spi_device *spi) |
84 | { |
85 | struct mc33880 *mc; |
86 | struct mc33880_platform_data *pdata; |
87 | int ret; |
88 | |
89 | pdata = spi->dev.platform_data; |
90 | if (!pdata || !pdata->base) { |
91 | dev_dbg(&spi->dev, "incorrect or missing platform data\n"); |
92 | return -EINVAL; |
93 | } |
94 | |
95 | /* |
96 | * bits_per_word cannot be configured in platform data |
97 | */ |
98 | spi->bits_per_word = 8; |
99 | |
100 | ret = spi_setup(spi); |
101 | if (ret < 0) |
102 | return ret; |
103 | |
104 | mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); |
105 | if (!mc) |
106 | return -ENOMEM; |
107 | |
108 | mutex_init(&mc->lock); |
109 | |
110 | dev_set_drvdata(&spi->dev, mc); |
111 | |
112 | mc->spi = spi; |
113 | |
114 | mc->chip.label = DRIVER_NAME, |
115 | mc->chip.set = mc33880_set; |
116 | mc->chip.base = pdata->base; |
117 | mc->chip.ngpio = PIN_NUMBER; |
118 | mc->chip.can_sleep = 1; |
119 | mc->chip.dev = &spi->dev; |
120 | mc->chip.owner = THIS_MODULE; |
121 | |
122 | mc->port_config = 0x00; |
123 | /* write twice, because during initialisation the first setting |
124 | * is just for testing SPI communication, and the second is the |
125 | * "real" configuration |
126 | */ |
127 | ret = mc33880_write_config(mc); |
128 | mc->port_config = 0x00; |
129 | if (!ret) |
130 | ret = mc33880_write_config(mc); |
131 | |
132 | if (ret) { |
133 | printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); |
134 | goto exit_destroy; |
135 | } |
136 | |
137 | ret = gpiochip_add(&mc->chip); |
138 | if (ret) |
139 | goto exit_destroy; |
140 | |
141 | return ret; |
142 | |
143 | exit_destroy: |
144 | dev_set_drvdata(&spi->dev, NULL); |
145 | mutex_destroy(&mc->lock); |
146 | kfree(mc); |
147 | return ret; |
148 | } |
149 | |
150 | static int __devexit mc33880_remove(struct spi_device *spi) |
151 | { |
152 | struct mc33880 *mc; |
153 | int ret; |
154 | |
155 | mc = dev_get_drvdata(&spi->dev); |
156 | if (mc == NULL) |
157 | return -ENODEV; |
158 | |
159 | dev_set_drvdata(&spi->dev, NULL); |
160 | |
161 | ret = gpiochip_remove(&mc->chip); |
162 | if (!ret) { |
163 | mutex_destroy(&mc->lock); |
164 | kfree(mc); |
165 | } else |
166 | dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", |
167 | ret); |
168 | |
169 | return ret; |
170 | } |
171 | |
172 | static struct spi_driver mc33880_driver = { |
173 | .driver = { |
174 | .name = DRIVER_NAME, |
175 | .owner = THIS_MODULE, |
176 | }, |
177 | .probe = mc33880_probe, |
178 | .remove = __devexit_p(mc33880_remove), |
179 | }; |
180 | |
181 | static int __init mc33880_init(void) |
182 | { |
183 | return spi_register_driver(&mc33880_driver); |
184 | } |
185 | /* register after spi postcore initcall and before |
186 | * subsys initcalls that may rely on these GPIOs |
187 | */ |
188 | subsys_initcall(mc33880_init); |
189 | |
190 | static void __exit mc33880_exit(void) |
191 | { |
192 | spi_unregister_driver(&mc33880_driver); |
193 | } |
194 | module_exit(mc33880_exit); |
195 | |
196 | MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); |
197 | MODULE_LICENSE("GPL v2"); |
198 | |
199 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9