aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2005-11-13 05:07:48 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2005-11-13 05:07:48 -0500
commitbd5d080ab99642e3245ef7cfa54490384c01d878 (patch)
treef3a6c1a55d9ecf9e6fcecc4e57fe36a1240c0ce4 /arch/arm
parente8b6f7f437a624fc2e2a2ec92fbeffdcf6f0e11e (diff)
[ARM] 3160/1: SharpSL: Add driver for Akita specific GPIOs
Patch from Richard Purdie Add a driver for the extra GPIOs found on the Sharp SL-C1000 (Akita). These GPIOs are found on a Maxim MAX7310 I2C i/o expander chip. A generic GPIO driver for the MAX7310 was attempted but this mini driver is a much simpler and much more effective solution avoiding several issues and complexity the generic driver had (as discussed on LKML). The platform device is required so the device parent can be set correctly which ensures the device is one of the last to suspend and first to resume. Whilst the i2c suspend/resume calls can be influenced, nothing guarantees this is easlier/later than the subsystems the gpios are used on which are all independent of i2c (sound, irda, video/backlight etc.). Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-pxa/Makefile1
-rw-r--r--arch/arm/mach-pxa/akita-ioexp.c223
2 files changed, 224 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index c83092e298a7..32526a0a6f86 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o
13obj-$(CONFIG_ARCH_PXA_IDP) += idp.o 13obj-$(CONFIG_ARCH_PXA_IDP) += idp.o
14obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o corgi_pm.o 14obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o corgi_pm.o
15obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o 15obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
16obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
16obj-$(CONFIG_MACH_POODLE) += poodle.o 17obj-$(CONFIG_MACH_POODLE) += poodle.o
17obj-$(CONFIG_MACH_TOSA) += tosa.o 18obj-$(CONFIG_MACH_TOSA) += tosa.o
18 19
diff --git a/arch/arm/mach-pxa/akita-ioexp.c b/arch/arm/mach-pxa/akita-ioexp.c
new file mode 100644
index 000000000000..f6d73cc01f78
--- /dev/null
+++ b/arch/arm/mach-pxa/akita-ioexp.c
@@ -0,0 +1,223 @@
1/*
2 * Support for the Extra GPIOs on the Sharp SL-C1000 (Akita)
3 * (uses a Maxim MAX7310 8 Port IO Expander)
4 *
5 * Copyright 2005 Openedhand Ltd.
6 *
7 * Author: Richard Purdie <richard@openedhand.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 */
14
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/platform_device.h>
18#include <linux/module.h>
19#include <linux/i2c.h>
20#include <linux/slab.h>
21#include <linux/workqueue.h>
22#include <asm/arch/akita.h>
23
24/* MAX7310 Regiser Map */
25#define MAX7310_INPUT 0x00
26#define MAX7310_OUTPUT 0x01
27#define MAX7310_POLINV 0x02
28#define MAX7310_IODIR 0x03 /* 1 = Input, 0 = Output */
29#define MAX7310_TIMEOUT 0x04
30
31/* Addresses to scan */
32static unsigned short normal_i2c[] = { 0x18, I2C_CLIENT_END };
33
34/* I2C Magic */
35I2C_CLIENT_INSMOD;
36
37static int max7310_write(struct i2c_client *client, int address, int data);
38static struct i2c_client max7310_template;
39static void akita_ioexp_work(void *private_);
40
41static struct device *akita_ioexp_device;
42static unsigned char ioexp_output_value = AKITA_IOEXP_IO_OUT;
43DECLARE_WORK(akita_ioexp, akita_ioexp_work, NULL);
44
45
46/*
47 * MAX7310 Access
48 */
49static int max7310_config(struct device *dev, int iomode, int polarity)
50{
51 int ret;
52 struct i2c_client *client = to_i2c_client(dev);
53
54 ret = max7310_write(client, MAX7310_POLINV, polarity);
55 if (ret < 0)
56 return ret;
57 ret = max7310_write(client, MAX7310_IODIR, iomode);
58 return ret;
59}
60
61static int max7310_set_ouputs(struct device *dev, int outputs)
62{
63 struct i2c_client *client = to_i2c_client(dev);
64
65 return max7310_write(client, MAX7310_OUTPUT, outputs);
66}
67
68/*
69 * I2C Functions
70 */
71static int max7310_write(struct i2c_client *client, int address, int value)
72{
73 u8 data[2];
74
75 data[0] = address & 0xff;
76 data[1] = value & 0xff;
77
78 if (i2c_master_send(client, data, 2) == 2)
79 return 0;
80 return -1;
81}
82
83static int max7310_detect(struct i2c_adapter *adapter, int address, int kind)
84{
85 struct i2c_client *new_client;
86 int err;
87
88 if (!(new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
89 return -ENOMEM;
90
91 max7310_template.adapter = adapter;
92 max7310_template.addr = address;
93
94 memcpy(new_client, &max7310_template, sizeof(struct i2c_client));
95
96 if ((err = i2c_attach_client(new_client))) {
97 kfree(new_client);
98 return err;
99 }
100
101 max7310_config(&new_client->dev, AKITA_IOEXP_IO_DIR, 0);
102 akita_ioexp_device = &new_client->dev;
103 schedule_work(&akita_ioexp);
104
105 return 0;
106}
107
108static int max7310_attach_adapter(struct i2c_adapter *adapter)
109{
110 return i2c_probe(adapter, &addr_data, max7310_detect);
111}
112
113static int max7310_detach_client(struct i2c_client *client)
114{
115 int err;
116
117 akita_ioexp_device = NULL;
118
119 if ((err = i2c_detach_client(client)))
120 return err;
121
122 kfree(client);
123 return 0;
124}
125
126static struct i2c_driver max7310_i2c_driver = {
127 .owner = THIS_MODULE,
128 .name = "akita-max7310",
129 .id = I2C_DRIVERID_AKITAIOEXP,
130 .flags = I2C_DF_NOTIFY,
131 .attach_adapter = max7310_attach_adapter,
132 .detach_client = max7310_detach_client,
133};
134
135static struct i2c_client max7310_template = {
136 name: "akita-max7310",
137 flags: I2C_CLIENT_ALLOW_USE,
138 driver: &max7310_i2c_driver,
139};
140
141void akita_set_ioexp(struct device *dev, unsigned char bit)
142{
143 ioexp_output_value |= bit;
144
145 if (akita_ioexp_device)
146 schedule_work(&akita_ioexp);
147 return;
148}
149
150void akita_reset_ioexp(struct device *dev, unsigned char bit)
151{
152 ioexp_output_value &= ~bit;
153
154 if (akita_ioexp_device)
155 schedule_work(&akita_ioexp);
156 return;
157}
158
159EXPORT_SYMBOL(akita_set_ioexp);
160EXPORT_SYMBOL(akita_reset_ioexp);
161
162static void akita_ioexp_work(void *private_)
163{
164 if (akita_ioexp_device)
165 max7310_set_ouputs(akita_ioexp_device, ioexp_output_value);
166}
167
168
169#ifdef CONFIG_PM
170static int akita_ioexp_suspend(struct platform_device *pdev, pm_message_t state)
171{
172 flush_scheduled_work();
173 return 0;
174}
175
176static int akita_ioexp_resume(struct platform_device *pdev)
177{
178 schedule_work(&akita_ioexp);
179 return 0;
180}
181#else
182#define akita_ioexp_suspend NULL
183#define akita_ioexp_resume NULL
184#endif
185
186static int __init akita_ioexp_probe(struct platform_device *pdev)
187{
188 return i2c_add_driver(&max7310_i2c_driver);
189}
190
191static int akita_ioexp_remove(struct platform_device *pdev)
192{
193 i2c_del_driver(&max7310_i2c_driver);
194 return 0;
195}
196
197static struct platform_driver akita_ioexp_driver = {
198 .probe = akita_ioexp_probe,
199 .remove = akita_ioexp_remove,
200 .suspend = akita_ioexp_suspend,
201 .resume = akita_ioexp_resume,
202 .driver = {
203 .name = "akita-ioexp",
204 },
205};
206
207static int __init akita_ioexp_init(void)
208{
209 return platform_driver_register(&akita_ioexp_driver);
210}
211
212static void __exit akita_ioexp_exit(void)
213{
214 platform_driver_unregister(&akita_ioexp_driver);
215}
216
217MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
218MODULE_DESCRIPTION("Akita IO-Expander driver");
219MODULE_LICENSE("GPL");
220
221fs_initcall(akita_ioexp_init);
222module_exit(akita_ioexp_exit);
223