]>
Commit | Line | Data |
---|---|---|
a5fd9139 SH |
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. | |
a5fd9139 SH |
13 | */ |
14 | ||
a5fd9139 SH |
15 | #include <linux/clk.h> |
16 | #include <linux/delay.h> | |
17 | #include <linux/io.h> | |
b0dceb6a | 18 | #include <linux/jiffies.h> |
18fd9e35 AS |
19 | #include <linux/module.h> |
20 | #include <linux/platform_device.h> | |
a5fd9139 | 21 | |
de0d6dbd | 22 | #include <linux/w1.h> |
a5fd9139 | 23 | |
a5fd9139 SH |
24 | /* |
25 | * MXC W1 Register offsets | |
26 | */ | |
18fd9e35 AS |
27 | #define MXC_W1_CONTROL 0x00 |
28 | # define MXC_W1_CONTROL_RDST BIT(3) | |
29 | # define MXC_W1_CONTROL_WR(x) BIT(5 - (x)) | |
30 | # define MXC_W1_CONTROL_PST BIT(6) | |
31 | # define MXC_W1_CONTROL_RPP BIT(7) | |
32 | #define MXC_W1_TIME_DIVIDER 0x02 | |
33 | #define MXC_W1_RESET 0x04 | |
b7ce0b5d | 34 | # define MXC_W1_RESET_RST BIT(0) |
a5fd9139 SH |
35 | |
36 | struct mxc_w1_device { | |
37 | void __iomem *regs; | |
a5fd9139 SH |
38 | struct clk *clk; |
39 | struct w1_bus_master bus_master; | |
40 | }; | |
41 | ||
42 | /* | |
43 | * this is the low level routine to | |
44 | * reset the device on the One Wire interface | |
45 | * on the hardware | |
46 | */ | |
47 | static u8 mxc_w1_ds2_reset_bus(void *data) | |
48 | { | |
a5fd9139 | 49 | struct mxc_w1_device *dev = data; |
b0dceb6a | 50 | unsigned long timeout; |
a5fd9139 | 51 | |
b0dceb6a | 52 | writeb(MXC_W1_CONTROL_RPP, dev->regs + MXC_W1_CONTROL); |
a5fd9139 | 53 | |
b0dceb6a AS |
54 | /* Wait for reset sequence 511+512us, use 1500us for sure */ |
55 | timeout = jiffies + usecs_to_jiffies(1500); | |
a5fd9139 | 56 | |
b0dceb6a | 57 | udelay(511 + 512); |
a5fd9139 | 58 | |
b0dceb6a AS |
59 | do { |
60 | u8 ctrl = readb(dev->regs + MXC_W1_CONTROL); | |
61 | ||
62 | /* PST bit is valid after the RPP bit is self-cleared */ | |
63 | if (!(ctrl & MXC_W1_CONTROL_RPP)) | |
64 | return !(ctrl & MXC_W1_CONTROL_PST); | |
65 | } while (time_is_after_jiffies(timeout)); | |
66 | ||
67 | return 1; | |
a5fd9139 SH |
68 | } |
69 | ||
70 | /* | |
71 | * this is the low level routine to read/write a bit on the One Wire | |
72 | * interface on the hardware. It does write 0 if parameter bit is set | |
73 | * to 0, otherwise a write 1/read. | |
74 | */ | |
75 | static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) | |
76 | { | |
f80b2581 AS |
77 | struct mxc_w1_device *dev = data; |
78 | unsigned long timeout; | |
a5fd9139 | 79 | |
f80b2581 | 80 | writeb(MXC_W1_CONTROL_WR(bit), dev->regs + MXC_W1_CONTROL); |
a5fd9139 | 81 | |
f80b2581 AS |
82 | /* Wait for read/write bit (60us, Max 120us), use 200us for sure */ |
83 | timeout = jiffies + usecs_to_jiffies(200); | |
a5fd9139 | 84 | |
f80b2581 AS |
85 | udelay(60); |
86 | ||
87 | do { | |
88 | u8 ctrl = readb(dev->regs + MXC_W1_CONTROL); | |
89 | ||
90 | /* RDST bit is valid after the WR1/RD bit is self-cleared */ | |
91 | if (!(ctrl & MXC_W1_CONTROL_WR(bit))) | |
92 | return !!(ctrl & MXC_W1_CONTROL_RDST); | |
93 | } while (time_is_after_jiffies(timeout)); | |
a5fd9139 | 94 | |
f80b2581 | 95 | return 0; |
a5fd9139 SH |
96 | } |
97 | ||
479e2bce | 98 | static int mxc_w1_probe(struct platform_device *pdev) |
a5fd9139 SH |
99 | { |
100 | struct mxc_w1_device *mdev; | |
71531f55 | 101 | unsigned long clkrate; |
a5fd9139 | 102 | struct resource *res; |
a0822637 | 103 | unsigned int clkdiv; |
001d1953 | 104 | int err; |
a5fd9139 | 105 | |
e5279ff6 JL |
106 | mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device), |
107 | GFP_KERNEL); | |
a5fd9139 SH |
108 | if (!mdev) |
109 | return -ENOMEM; | |
110 | ||
e5279ff6 JL |
111 | mdev->clk = devm_clk_get(&pdev->dev, NULL); |
112 | if (IS_ERR(mdev->clk)) | |
113 | return PTR_ERR(mdev->clk); | |
a5fd9139 | 114 | |
9cb28c1b SP |
115 | err = clk_prepare_enable(mdev->clk); |
116 | if (err) | |
117 | return err; | |
118 | ||
71531f55 AS |
119 | clkrate = clk_get_rate(mdev->clk); |
120 | if (clkrate < 10000000) | |
121 | dev_warn(&pdev->dev, | |
122 | "Low clock frequency causes improper function\n"); | |
123 | ||
124 | clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000); | |
125 | clkrate /= clkdiv; | |
126 | if ((clkrate < 980000) || (clkrate > 1020000)) | |
127 | dev_warn(&pdev->dev, | |
128 | "Incorrect time base frequency %lu Hz\n", clkrate); | |
a5fd9139 | 129 | |
e5279ff6 | 130 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
388f7bd2 | 131 | mdev->regs = devm_ioremap_resource(&pdev->dev, res); |
9cb28c1b SP |
132 | if (IS_ERR(mdev->regs)) { |
133 | err = PTR_ERR(mdev->regs); | |
134 | goto out_disable_clk; | |
135 | } | |
001d1953 | 136 | |
b7ce0b5d AS |
137 | /* Software reset 1-Wire module */ |
138 | writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET); | |
139 | writeb(0, mdev->regs + MXC_W1_RESET); | |
140 | ||
fc945d6e | 141 | writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); |
a5fd9139 SH |
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 | ||
001d1953 | 147 | platform_set_drvdata(pdev, mdev); |
a5fd9139 | 148 | |
001d1953 | 149 | err = w1_add_master_device(&mdev->bus_master); |
a5fd9139 | 150 | if (err) |
9cb28c1b | 151 | goto out_disable_clk; |
a5fd9139 | 152 | |
9cb28c1b SP |
153 | return 0; |
154 | ||
155 | out_disable_clk: | |
156 | clk_disable_unprepare(mdev->clk); | |
001d1953 | 157 | return err; |
a5fd9139 SH |
158 | } |
159 | ||
160 | /* | |
161 | * disassociate the w1 device from the driver | |
162 | */ | |
82849a93 | 163 | static int mxc_w1_remove(struct platform_device *pdev) |
a5fd9139 SH |
164 | { |
165 | struct mxc_w1_device *mdev = platform_get_drvdata(pdev); | |
a5fd9139 SH |
166 | |
167 | w1_remove_master_device(&mdev->bus_master); | |
168 | ||
60178b63 | 169 | clk_disable_unprepare(mdev->clk); |
a5fd9139 | 170 | |
a5fd9139 SH |
171 | return 0; |
172 | } | |
173 | ||
0a56c0e1 | 174 | static const struct of_device_id mxc_w1_dt_ids[] = { |
28c55dc1 MF |
175 | { .compatible = "fsl,imx21-owire" }, |
176 | { /* sentinel */ } | |
177 | }; | |
178 | MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); | |
179 | ||
a5fd9139 SH |
180 | static struct platform_driver mxc_w1_driver = { |
181 | .driver = { | |
28c55dc1 MF |
182 | .name = "mxc_w1", |
183 | .of_match_table = mxc_w1_dt_ids, | |
a5fd9139 SH |
184 | }, |
185 | .probe = mxc_w1_probe, | |
10532fe7 | 186 | .remove = mxc_w1_remove, |
a5fd9139 | 187 | }; |
fd21bfcc | 188 | module_platform_driver(mxc_w1_driver); |
a5fd9139 SH |
189 | |
190 | MODULE_LICENSE("GPL"); | |
191 | MODULE_AUTHOR("Freescale Semiconductors Inc"); | |
192 | MODULE_DESCRIPTION("Driver for One-Wire on MXC"); |