aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorPhilipp Zabel <philipp.zabel@gmail.com>2008-04-12 08:25:41 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-04-19 06:29:08 -0400
commit5dc3339aa5ba29593ea57814049ddca8c12831c8 (patch)
tree0481d4344d0cf4052effce8605706414ce63ce8a /drivers/mfd
parent481ea5a16aaabe2570d56b5a4d29fa80adc8008b (diff)
[ARM] 4964/1: htc-pasic3: MFD driver for PASIC3 LED control + DS1WM chip
This driver will provide registers, clocks and GPIOs of the HTC PASIC3 (AIC3) and PASIC2 (AIC2) chips to the ds1wm and leds-pasic3 drivers. Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/htc-pasic3.c265
3 files changed, 274 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 284b2dc0344..2566479937c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -30,6 +30,14 @@ config HTC_EGPIO
30 several HTC phones. It provides basic support for input 30 several HTC phones. It provides basic support for input
31 pins, output pins, and irqs. 31 pins, output pins, and irqs.
32 32
33config HTC_PASIC3
34 tristate "HTC PASIC3 LED/DS1WM chip support"
35 help
36 This core driver provides register access for the LED/DS1WM
37 chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
38 HTC Magician devices, respectively. Actual functionality is
39 handled by the leds-pasic3 and ds1wm drivers.
40
33endmenu 41endmenu
34 42
35menu "Multimedia Capabilities Port drivers" 43menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f81f8d2e2c0..eef4e26807d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o
6obj-$(CONFIG_MFD_ASIC3) += asic3.o 6obj-$(CONFIG_MFD_ASIC3) += asic3.o
7 7
8obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o 8obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
9obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
9 10
10obj-$(CONFIG_MCP) += mcp-core.o 11obj-$(CONFIG_MCP) += mcp-core.o
11obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o 12obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
new file mode 100644
index 00000000000..af66f4f2830
--- /dev/null
+++ b/drivers/mfd/htc-pasic3.c
@@ -0,0 +1,265 @@
1/*
2 * Core driver for HTC PASIC3 LED/DS1WM chip.
3 *
4 * Copyright (C) 2006 Philipp Zabel <philipp.zabel@gmail.com>
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; version 2 of the License.
9 */
10
11#include <linux/init.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14
15#include <linux/ds1wm.h>
16#include <linux/gpio.h>
17#include <linux/io.h>
18#include <linux/irq.h>
19#include <linux/interrupt.h>
20#include <linux/mfd/htc-pasic3.h>
21
22#include <asm/arch/pxa-regs.h>
23
24struct pasic3_data {
25 void __iomem *mapping;
26 unsigned int bus_shift;
27 struct platform_device *ds1wm_pdev;
28 struct platform_device *led_pdev;
29};
30
31#define REG_ADDR 5
32#define REG_DATA 6
33#define NUM_REGS 7
34
35#define READ_MODE 0x80
36
37/*
38 * write to a secondary register on the PASIC3
39 */
40void pasic3_write_register(struct device *dev, u32 reg, u8 val)
41{
42 struct pasic3_data *asic = dev->driver_data;
43 int bus_shift = asic->bus_shift;
44 void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
45 void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
46
47 __raw_writeb(~READ_MODE & reg, addr);
48 __raw_writeb(val, data);
49}
50EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */
51
52/*
53 * read from a secondary register on the PASIC3
54 */
55u8 pasic3_read_register(struct device *dev, u32 reg)
56{
57 struct pasic3_data *asic = dev->driver_data;
58 int bus_shift = asic->bus_shift;
59 void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
60 void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
61
62 __raw_writeb(READ_MODE | reg, addr);
63 return __raw_readb(data);
64}
65EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */
66
67/*
68 * LEDs
69 */
70
71static int led_device_add(struct device *pasic3_dev,
72 const struct pasic3_leds_machinfo *pdata)
73{
74 struct pasic3_data *asic = pasic3_dev->driver_data;
75 struct platform_device *pdev;
76 int ret;
77
78 pdev = platform_device_alloc("pasic3-led", -1);
79 if (!pdev) {
80 dev_dbg(pasic3_dev, "failed to allocate LED platform device\n");
81 return -ENOMEM;
82 }
83
84 ret = platform_device_add_data(pdev, pdata,
85 sizeof(struct pasic3_leds_machinfo));
86 if (ret < 0) {
87 dev_dbg(pasic3_dev, "failed to add LED platform data\n");
88 goto exit_pdev_put;
89 }
90
91 pdev->dev.parent = pasic3_dev;
92 ret = platform_device_add(pdev);
93 if (ret < 0) {
94 dev_dbg(pasic3_dev, "failed to add LED platform device\n");
95 goto exit_pdev_put;
96 }
97
98 asic->led_pdev = pdev;
99 return 0;
100
101exit_pdev_put:
102 platform_device_put(pdev);
103 return ret;
104}
105
106/*
107 * DS1WM
108 */
109
110static void ds1wm_enable(struct platform_device *pdev)
111{
112 struct device *dev = pdev->dev.parent;
113 int c;
114
115 c = pasic3_read_register(dev, 0x28);
116 pasic3_write_register(dev, 0x28, c & 0x7f);
117
118 dev_dbg(dev, "DS1WM OWM_EN low (active) %02x\n", c & 0x7f);
119}
120
121static void ds1wm_disable(struct platform_device *pdev)
122{
123 struct device *dev = pdev->dev.parent;
124 int c;
125
126 c = pasic3_read_register(dev, 0x28);
127 pasic3_write_register(dev, 0x28, c | 0x80);
128
129 dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x\n", c | 0x80);
130}
131
132static struct ds1wm_platform_data ds1wm_pdata = {
133 .bus_shift = 2,
134 .enable = ds1wm_enable,
135 .disable = ds1wm_disable,
136};
137
138static int ds1wm_device_add(struct device *pasic3_dev, int bus_shift)
139{
140 struct pasic3_data *asic = pasic3_dev->driver_data;
141 struct platform_device *pdev;
142 int ret;
143
144 pdev = platform_device_alloc("ds1wm", -1);
145 if (!pdev) {
146 dev_dbg(pasic3_dev, "failed to allocate DS1WM platform device\n");
147 return -ENOMEM;
148 }
149
150 ret = platform_device_add_resources(pdev, pdev->resource,
151 pdev->num_resources);
152 if (ret < 0) {
153 dev_dbg(pasic3_dev, "failed to add DS1WM resources\n");
154 goto exit_pdev_put;
155 }
156
157 ds1wm_pdata.bus_shift = asic->bus_shift;
158 ret = platform_device_add_data(pdev, &ds1wm_pdata,
159 sizeof(struct ds1wm_platform_data));
160 if (ret < 0) {
161 dev_dbg(pasic3_dev, "failed to add DS1WM platform data\n");
162 goto exit_pdev_put;
163 }
164
165 pdev->dev.parent = pasic3_dev;
166 ret = platform_device_add(pdev);
167 if (ret < 0) {
168 dev_dbg(pasic3_dev, "failed to add DS1WM platform device\n");
169 goto exit_pdev_put;
170 }
171
172 asic->ds1wm_pdev = pdev;
173 return 0;
174
175exit_pdev_put:
176 platform_device_put(pdev);
177 return ret;
178}
179
180static int __init pasic3_probe(struct platform_device *pdev)
181{
182 struct pasic3_platform_data *pdata = pdev->dev.platform_data;
183 struct device *dev = &pdev->dev;
184 struct pasic3_data *asic;
185 struct resource *r;
186 int ret;
187
188 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
189 if (!r)
190 return -ENXIO;
191
192 if (!request_mem_region(r->start, r->end - r->start + 1, "pasic3"))
193 return -EBUSY;
194
195 asic = kzalloc(sizeof(struct pasic3_data), GFP_KERNEL);
196 if (!asic)
197 return -ENOMEM;
198
199 platform_set_drvdata(pdev, asic);
200
201 if (pdata && pdata->bus_shift)
202 asic->bus_shift = pdata->bus_shift;
203 else
204 asic->bus_shift = 2;
205
206 asic->mapping = ioremap(r->start, r->end - r->start + 1);
207 if (!asic->mapping) {
208 dev_err(dev, "couldn't ioremap PASIC3\n");
209 kfree(asic);
210 return -ENOMEM;
211 }
212
213 ret = ds1wm_device_add(dev, asic->bus_shift);
214 if (ret < 0)
215 dev_warn(dev, "failed to register DS1WM\n");
216
217 if (pdata->led_pdata) {
218 ret = led_device_add(dev, pdata->led_pdata);
219 if (ret < 0)
220 dev_warn(dev, "failed to register LED device\n");
221 }
222
223 return 0;
224}
225
226static int pasic3_remove(struct platform_device *pdev)
227{
228 struct pasic3_data *asic = platform_get_drvdata(pdev);
229 struct resource *r;
230
231 if (asic->led_pdev)
232 platform_device_unregister(asic->led_pdev);
233 if (asic->ds1wm_pdev)
234 platform_device_unregister(asic->ds1wm_pdev);
235
236 iounmap(asic->mapping);
237 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
238 release_mem_region(r->start, r->end - r->start + 1);
239 kfree(asic);
240 return 0;
241}
242
243static struct platform_driver pasic3_driver = {
244 .driver = {
245 .name = "pasic3",
246 },
247 .remove = pasic3_remove,
248};
249
250static int __init pasic3_base_init(void)
251{
252 return platform_driver_probe(&pasic3_driver, pasic3_probe);
253}
254
255static void __exit pasic3_base_exit(void)
256{
257 platform_driver_unregister(&pasic3_driver);
258}
259
260module_init(pasic3_base_init);
261module_exit(pasic3_base_exit);
262
263MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
264MODULE_DESCRIPTION("Core driver for HTC PASIC3");
265MODULE_LICENSE("GPL");