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