diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2009-01-07 21:08:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-08 11:31:13 -0500 |
commit | a5fd9139f74c722a190b3bd69bbd611a8d91b388 (patch) | |
tree | b2c56580746d94d7dfc7caf6bff2812314f53413 /drivers/w1 | |
parent | 09f50c95425318232c534d931d8c28b96a3ce2c5 (diff) |
w1: add 1-wire master driver for i.MX27 / i.MX31
This patch adds support for the 1-wire master interface for i.MX27 and
i.MX31.
Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/masters/Kconfig | 6 | ||||
-rw-r--r-- | drivers/w1/masters/Makefile | 2 | ||||
-rw-r--r-- | drivers/w1/masters/mxc_w1.c | 211 |
3 files changed, 219 insertions, 0 deletions
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 90616822cd20..96d2f8e4c275 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig | |||
@@ -34,6 +34,12 @@ config W1_MASTER_DS2482 | |||
34 | This driver can also be built as a module. If so, the module | 34 | This driver can also be built as a module. If so, the module |
35 | will be called ds2482. | 35 | will be called ds2482. |
36 | 36 | ||
37 | config W1_MASTER_MXC | ||
38 | tristate "Freescale MXC 1-wire busmaster" | ||
39 | depends on W1 && ARCH_MXC | ||
40 | help | ||
41 | Say Y here to enable MXC 1-wire host | ||
42 | |||
37 | config W1_MASTER_DS1WM | 43 | config W1_MASTER_DS1WM |
38 | tristate "Maxim DS1WM 1-wire busmaster" | 44 | tristate "Maxim DS1WM 1-wire busmaster" |
39 | depends on W1 && ARM && HAVE_CLK | 45 | depends on W1 && ARM && HAVE_CLK |
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index bc4714a75f3a..c5a3e96fcbab 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile | |||
@@ -5,6 +5,8 @@ | |||
5 | obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o | 5 | obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o |
6 | obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o | 6 | obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o |
7 | obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o | 7 | obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o |
8 | obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o | ||
9 | |||
8 | obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o | 10 | obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o |
9 | obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o | 11 | obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o |
10 | obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o | 12 | obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o |
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c new file mode 100644 index 000000000000..b9d74d0b353e --- /dev/null +++ b/drivers/w1/masters/mxc_w1.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * Copyright 2008 Luotao Fu, kernel@pengutronix.de | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version 2 | ||
8 | * of the License, or (at your option) any later version. | ||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include "../w1.h" | ||
28 | #include "../w1_int.h" | ||
29 | #include "../w1_log.h" | ||
30 | |||
31 | /* According to the mx27 Datasheet the reset procedure should take up to about | ||
32 | * 1350us. We set the timeout to 500*100us = 50ms for sure */ | ||
33 | #define MXC_W1_RESET_TIMEOUT 500 | ||
34 | |||
35 | /* | ||
36 | * MXC W1 Register offsets | ||
37 | */ | ||
38 | #define MXC_W1_CONTROL 0x00 | ||
39 | #define MXC_W1_TIME_DIVIDER 0x02 | ||
40 | #define MXC_W1_RESET 0x04 | ||
41 | #define MXC_W1_COMMAND 0x06 | ||
42 | #define MXC_W1_TXRX 0x08 | ||
43 | #define MXC_W1_INTERRUPT 0x0A | ||
44 | #define MXC_W1_INTERRUPT_EN 0x0C | ||
45 | |||
46 | struct mxc_w1_device { | ||
47 | void __iomem *regs; | ||
48 | unsigned int clkdiv; | ||
49 | struct clk *clk; | ||
50 | struct w1_bus_master bus_master; | ||
51 | }; | ||
52 | |||
53 | /* | ||
54 | * this is the low level routine to | ||
55 | * reset the device on the One Wire interface | ||
56 | * on the hardware | ||
57 | */ | ||
58 | static u8 mxc_w1_ds2_reset_bus(void *data) | ||
59 | { | ||
60 | u8 reg_val; | ||
61 | unsigned int timeout_cnt = 0; | ||
62 | struct mxc_w1_device *dev = data; | ||
63 | |||
64 | __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); | ||
65 | |||
66 | while (1) { | ||
67 | reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); | ||
68 | |||
69 | if (((reg_val >> 7) & 0x1) == 0 || | ||
70 | timeout_cnt > MXC_W1_RESET_TIMEOUT) | ||
71 | break; | ||
72 | else | ||
73 | timeout_cnt++; | ||
74 | |||
75 | udelay(100); | ||
76 | } | ||
77 | return (reg_val >> 7) & 0x1; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * this is the low level routine to read/write a bit on the One Wire | ||
82 | * interface on the hardware. It does write 0 if parameter bit is set | ||
83 | * to 0, otherwise a write 1/read. | ||
84 | */ | ||
85 | static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) | ||
86 | { | ||
87 | struct mxc_w1_device *mdev = data; | ||
88 | void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL; | ||
89 | unsigned int timeout_cnt = 400; /* Takes max. 120us according to | ||
90 | * datasheet. | ||
91 | */ | ||
92 | |||
93 | __raw_writeb((1 << (5 - bit)), ctrl_addr); | ||
94 | |||
95 | while (timeout_cnt--) { | ||
96 | if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) | ||
97 | break; | ||
98 | |||
99 | udelay(1); | ||
100 | } | ||
101 | |||
102 | return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; | ||
103 | } | ||
104 | |||
105 | static int __init mxc_w1_probe(struct platform_device *pdev) | ||
106 | { | ||
107 | struct mxc_w1_device *mdev; | ||
108 | struct resource *res; | ||
109 | int err = 0; | ||
110 | |||
111 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
112 | if (!res) | ||
113 | return -ENODEV; | ||
114 | |||
115 | mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); | ||
116 | if (!mdev) | ||
117 | return -ENOMEM; | ||
118 | |||
119 | mdev->clk = clk_get(&pdev->dev, "owire_clk"); | ||
120 | if (!mdev->clk) { | ||
121 | err = -ENODEV; | ||
122 | goto failed_clk; | ||
123 | } | ||
124 | |||
125 | mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; | ||
126 | |||
127 | res = request_mem_region(res->start, resource_size(res), | ||
128 | "mxc_w1"); | ||
129 | if (!res) { | ||
130 | err = -EBUSY; | ||
131 | goto failed_req; | ||
132 | } | ||
133 | |||
134 | mdev->regs = ioremap(res->start, resource_size(res)); | ||
135 | if (!mdev->regs) { | ||
136 | printk(KERN_ERR "Cannot map frame buffer registers\n"); | ||
137 | goto failed_ioremap; | ||
138 | } | ||
139 | |||
140 | clk_enable(mdev->clk); | ||
141 | __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); | ||
142 | |||
143 | mdev->bus_master.data = mdev; | ||
144 | mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; | ||
145 | mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; | ||
146 | |||
147 | err = w1_add_master_device(&mdev->bus_master); | ||
148 | |||
149 | if (err) | ||
150 | goto failed_add; | ||
151 | |||
152 | platform_set_drvdata(pdev, mdev); | ||
153 | return 0; | ||
154 | |||
155 | failed_add: | ||
156 | iounmap(mdev->regs); | ||
157 | failed_ioremap: | ||
158 | release_mem_region(res->start, resource_size(res)); | ||
159 | failed_req: | ||
160 | clk_put(mdev->clk); | ||
161 | failed_clk: | ||
162 | kfree(mdev); | ||
163 | return err; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * disassociate the w1 device from the driver | ||
168 | */ | ||
169 | static int mxc_w1_remove(struct platform_device *pdev) | ||
170 | { | ||
171 | struct mxc_w1_device *mdev = platform_get_drvdata(pdev); | ||
172 | struct resource *res; | ||
173 | |||
174 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
175 | |||
176 | w1_remove_master_device(&mdev->bus_master); | ||
177 | |||
178 | iounmap(mdev->regs); | ||
179 | release_mem_region(res->start, resource_size(res)); | ||
180 | clk_disable(mdev->clk); | ||
181 | clk_put(mdev->clk); | ||
182 | |||
183 | platform_set_drvdata(pdev, NULL); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static struct platform_driver mxc_w1_driver = { | ||
189 | .driver = { | ||
190 | .name = "mxc_w1", | ||
191 | }, | ||
192 | .probe = mxc_w1_probe, | ||
193 | .remove = mxc_w1_remove, | ||
194 | }; | ||
195 | |||
196 | static int __init mxc_w1_init(void) | ||
197 | { | ||
198 | return platform_driver_register(&mxc_w1_driver); | ||
199 | } | ||
200 | |||
201 | static void mxc_w1_exit(void) | ||
202 | { | ||
203 | platform_driver_unregister(&mxc_w1_driver); | ||
204 | } | ||
205 | |||
206 | module_init(mxc_w1_init); | ||
207 | module_exit(mxc_w1_exit); | ||
208 | |||
209 | MODULE_LICENSE("GPL"); | ||
210 | MODULE_AUTHOR("Freescale Semiconductors Inc"); | ||
211 | MODULE_DESCRIPTION("Driver for One-Wire on MXC"); | ||