Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
htc-pasic3.c
Go to the documentation of this file.
1 /*
2  * Core driver for HTC PASIC3 LED/DS1WM chip.
3  *
4  * Copyright (C) 2006 Philipp Zabel <[email protected]>
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/gpio.h>
16 #include <linux/io.h>
17 #include <linux/irq.h>
18 #include <linux/interrupt.h>
19 #include <linux/mfd/core.h>
20 #include <linux/mfd/ds1wm.h>
21 #include <linux/mfd/htc-pasic3.h>
22 #include <linux/slab.h>
23 
24 struct pasic3_data {
26  unsigned int bus_shift;
27 };
28 
29 #define REG_ADDR 5
30 #define REG_DATA 6
31 
32 #define READ_MODE 0x80
33 
34 /*
35  * write to a secondary register on the PASIC3
36  */
38 {
39  struct pasic3_data *asic = dev_get_drvdata(dev);
40  int bus_shift = asic->bus_shift;
41  void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
42  void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
43 
44  __raw_writeb(~READ_MODE & reg, addr);
45  __raw_writeb(val, data);
46 }
47 EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */
48 
49 /*
50  * read from a secondary register on the PASIC3
51  */
53 {
54  struct pasic3_data *asic = dev_get_drvdata(dev);
55  int bus_shift = asic->bus_shift;
56  void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
57  void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
58 
59  __raw_writeb(READ_MODE | reg, addr);
60  return __raw_readb(data);
61 }
62 EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */
63 
64 /*
65  * LEDs
66  */
67 
68 static struct mfd_cell led_cell __initdata = {
69  .name = "leds-pasic3",
70 };
71 
72 /*
73  * DS1WM
74  */
75 
76 static int ds1wm_enable(struct platform_device *pdev)
77 {
78  struct device *dev = pdev->dev.parent;
79  int c;
80 
81  c = pasic3_read_register(dev, 0x28);
82  pasic3_write_register(dev, 0x28, c & 0x7f);
83 
84  dev_dbg(dev, "DS1WM OWM_EN low (active) %02x\n", c & 0x7f);
85  return 0;
86 }
87 
88 static int ds1wm_disable(struct platform_device *pdev)
89 {
90  struct device *dev = pdev->dev.parent;
91  int c;
92 
93  c = pasic3_read_register(dev, 0x28);
94  pasic3_write_register(dev, 0x28, c | 0x80);
95 
96  dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x\n", c | 0x80);
97  return 0;
98 }
99 
100 static struct ds1wm_driver_data ds1wm_pdata = {
101  .active_high = 0,
102  .reset_recover_delay = 1,
103 };
104 
105 static struct resource ds1wm_resources[] __initdata = {
106  [0] = {
107  .start = 0,
108  .flags = IORESOURCE_MEM,
109  },
110  [1] = {
111  .start = 0,
112  .end = 0,
113  .flags = IORESOURCE_IRQ,
114  },
115 };
116 
117 static struct mfd_cell ds1wm_cell __initdata = {
118  .name = "ds1wm",
119  .enable = ds1wm_enable,
120  .disable = ds1wm_disable,
121  .platform_data = &ds1wm_pdata,
122  .pdata_size = sizeof(ds1wm_pdata),
123  .num_resources = 2,
124  .resources = ds1wm_resources,
125 };
126 
127 static int __init pasic3_probe(struct platform_device *pdev)
128 {
129  struct pasic3_platform_data *pdata = pdev->dev.platform_data;
130  struct device *dev = &pdev->dev;
131  struct pasic3_data *asic;
132  struct resource *r;
133  int ret;
134  int irq = 0;
135 
137  if (r) {
138  ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags &
140  irq = r->start;
141  }
142 
144  if (!r)
145  return -ENXIO;
146 
147  if (!request_mem_region(r->start, resource_size(r), "pasic3"))
148  return -EBUSY;
149 
150  asic = kzalloc(sizeof(struct pasic3_data), GFP_KERNEL);
151  if (!asic)
152  return -ENOMEM;
153 
154  platform_set_drvdata(pdev, asic);
155 
156  asic->mapping = ioremap(r->start, resource_size(r));
157  if (!asic->mapping) {
158  dev_err(dev, "couldn't ioremap PASIC3\n");
159  kfree(asic);
160  return -ENOMEM;
161  }
162 
163  /* calculate bus shift from mem resource */
164  asic->bus_shift = (resource_size(r) - 5) >> 3;
165 
166  if (pdata && pdata->clock_rate) {
167  ds1wm_pdata.clock_rate = pdata->clock_rate;
168  /* the first 5 PASIC3 registers control the DS1WM */
169  ds1wm_resources[0].end = (5 << asic->bus_shift) - 1;
170  ret = mfd_add_devices(&pdev->dev, pdev->id,
171  &ds1wm_cell, 1, r, irq, NULL);
172  if (ret < 0)
173  dev_warn(dev, "failed to register DS1WM\n");
174  }
175 
176  if (pdata && pdata->led_pdata) {
177  led_cell.platform_data = pdata->led_pdata;
178  led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo);
179  ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r,
180  0, NULL);
181  if (ret < 0)
182  dev_warn(dev, "failed to register LED device\n");
183  }
184 
185  return 0;
186 }
187 
188 static int pasic3_remove(struct platform_device *pdev)
189 {
190  struct pasic3_data *asic = platform_get_drvdata(pdev);
191  struct resource *r;
192 
193  mfd_remove_devices(&pdev->dev);
194 
195  iounmap(asic->mapping);
197  release_mem_region(r->start, resource_size(r));
198  kfree(asic);
199  return 0;
200 }
201 
202 MODULE_ALIAS("platform:pasic3");
203 
204 static struct platform_driver pasic3_driver = {
205  .driver = {
206  .name = "pasic3",
207  },
208  .remove = pasic3_remove,
209 };
210 
211 static int __init pasic3_base_init(void)
212 {
213  return platform_driver_probe(&pasic3_driver, pasic3_probe);
214 }
215 
216 static void __exit pasic3_base_exit(void)
217 {
218  platform_driver_unregister(&pasic3_driver);
219 }
220 
221 module_init(pasic3_base_init);
222 module_exit(pasic3_base_exit);
223 
224 MODULE_AUTHOR("Philipp Zabel <[email protected]>");
225 MODULE_DESCRIPTION("Core driver for HTC PASIC3");
226 MODULE_LICENSE("GPL");