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