1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a5fd9139SSascha Hauer /*
3a5fd9139SSascha Hauer * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
4a5fd9139SSascha Hauer * Copyright 2008 Luotao Fu, kernel@pengutronix.de
5a5fd9139SSascha Hauer */
6a5fd9139SSascha Hauer
7a5fd9139SSascha Hauer #include <linux/clk.h>
8a5fd9139SSascha Hauer #include <linux/delay.h>
9a5fd9139SSascha Hauer #include <linux/io.h>
10c9723750SMartin Fuzzey #include <linux/ktime.h>
1118fd9e35SAlexander Shiyan #include <linux/module.h>
12ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
1318fd9e35SAlexander Shiyan #include <linux/platform_device.h>
14a5fd9139SSascha Hauer
15de0d6dbdSAndrew F. Davis #include <linux/w1.h>
16a5fd9139SSascha Hauer
17a5fd9139SSascha Hauer /*
18a5fd9139SSascha Hauer * MXC W1 Register offsets
19a5fd9139SSascha Hauer */
20a5fd9139SSascha Hauer #define MXC_W1_CONTROL 0x00
2118fd9e35SAlexander Shiyan # define MXC_W1_CONTROL_RDST BIT(3)
2218fd9e35SAlexander Shiyan # define MXC_W1_CONTROL_WR(x) BIT(5 - (x))
2318fd9e35SAlexander Shiyan # define MXC_W1_CONTROL_PST BIT(6)
2418fd9e35SAlexander Shiyan # define MXC_W1_CONTROL_RPP BIT(7)
25a5fd9139SSascha Hauer #define MXC_W1_TIME_DIVIDER 0x02
26a5fd9139SSascha Hauer #define MXC_W1_RESET 0x04
27b7ce0b5dSAlexander Shiyan # define MXC_W1_RESET_RST BIT(0)
28a5fd9139SSascha Hauer
29a5fd9139SSascha Hauer struct mxc_w1_device {
30a5fd9139SSascha Hauer void __iomem *regs;
31a5fd9139SSascha Hauer struct clk *clk;
32a5fd9139SSascha Hauer struct w1_bus_master bus_master;
33a5fd9139SSascha Hauer };
34a5fd9139SSascha Hauer
35a5fd9139SSascha Hauer /*
36a5fd9139SSascha Hauer * this is the low level routine to
37a5fd9139SSascha Hauer * reset the device on the One Wire interface
38a5fd9139SSascha Hauer * on the hardware
39a5fd9139SSascha Hauer */
mxc_w1_ds2_reset_bus(void * data)40a5fd9139SSascha Hauer static u8 mxc_w1_ds2_reset_bus(void *data)
41a5fd9139SSascha Hauer {
42a5fd9139SSascha Hauer struct mxc_w1_device *dev = data;
43c9723750SMartin Fuzzey ktime_t timeout;
44a5fd9139SSascha Hauer
45b0dceb6aSAlexander Shiyan writeb(MXC_W1_CONTROL_RPP, dev->regs + MXC_W1_CONTROL);
46a5fd9139SSascha Hauer
47b0dceb6aSAlexander Shiyan /* Wait for reset sequence 511+512us, use 1500us for sure */
48c9723750SMartin Fuzzey timeout = ktime_add_us(ktime_get(), 1500);
49a5fd9139SSascha Hauer
50b0dceb6aSAlexander Shiyan udelay(511 + 512);
51a5fd9139SSascha Hauer
52b0dceb6aSAlexander Shiyan do {
53b0dceb6aSAlexander Shiyan u8 ctrl = readb(dev->regs + MXC_W1_CONTROL);
54b0dceb6aSAlexander Shiyan
55b0dceb6aSAlexander Shiyan /* PST bit is valid after the RPP bit is self-cleared */
56b0dceb6aSAlexander Shiyan if (!(ctrl & MXC_W1_CONTROL_RPP))
57b0dceb6aSAlexander Shiyan return !(ctrl & MXC_W1_CONTROL_PST);
58c9723750SMartin Fuzzey } while (ktime_before(ktime_get(), timeout));
59b0dceb6aSAlexander Shiyan
60b0dceb6aSAlexander Shiyan return 1;
61a5fd9139SSascha Hauer }
62a5fd9139SSascha Hauer
63a5fd9139SSascha Hauer /*
64a5fd9139SSascha Hauer * this is the low level routine to read/write a bit on the One Wire
65a5fd9139SSascha Hauer * interface on the hardware. It does write 0 if parameter bit is set
66a5fd9139SSascha Hauer * to 0, otherwise a write 1/read.
67a5fd9139SSascha Hauer */
mxc_w1_ds2_touch_bit(void * data,u8 bit)68a5fd9139SSascha Hauer static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
69a5fd9139SSascha Hauer {
70f80b2581SAlexander Shiyan struct mxc_w1_device *dev = data;
71c9723750SMartin Fuzzey ktime_t timeout;
72a5fd9139SSascha Hauer
73f80b2581SAlexander Shiyan writeb(MXC_W1_CONTROL_WR(bit), dev->regs + MXC_W1_CONTROL);
74a5fd9139SSascha Hauer
75f80b2581SAlexander Shiyan /* Wait for read/write bit (60us, Max 120us), use 200us for sure */
76c9723750SMartin Fuzzey timeout = ktime_add_us(ktime_get(), 200);
77a5fd9139SSascha Hauer
78f80b2581SAlexander Shiyan udelay(60);
79a5fd9139SSascha Hauer
80f80b2581SAlexander Shiyan do {
81f80b2581SAlexander Shiyan u8 ctrl = readb(dev->regs + MXC_W1_CONTROL);
82f80b2581SAlexander Shiyan
83f80b2581SAlexander Shiyan /* RDST bit is valid after the WR1/RD bit is self-cleared */
84f80b2581SAlexander Shiyan if (!(ctrl & MXC_W1_CONTROL_WR(bit)))
85f80b2581SAlexander Shiyan return !!(ctrl & MXC_W1_CONTROL_RDST);
86c9723750SMartin Fuzzey } while (ktime_before(ktime_get(), timeout));
87f80b2581SAlexander Shiyan
88f80b2581SAlexander Shiyan return 0;
89a5fd9139SSascha Hauer }
90a5fd9139SSascha Hauer
mxc_w1_probe(struct platform_device * pdev)91479e2bceSBill Pemberton static int mxc_w1_probe(struct platform_device *pdev)
92a5fd9139SSascha Hauer {
93a5fd9139SSascha Hauer struct mxc_w1_device *mdev;
9471531f55SAlexander Shiyan unsigned long clkrate;
95a0822637SAlexander Shiyan unsigned int clkdiv;
96001d1953SAlexander Shiyan int err;
97a5fd9139SSascha Hauer
98e5279ff6SJulia Lawall mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device),
99e5279ff6SJulia Lawall GFP_KERNEL);
100a5fd9139SSascha Hauer if (!mdev)
101a5fd9139SSascha Hauer return -ENOMEM;
102a5fd9139SSascha Hauer
103e5279ff6SJulia Lawall mdev->clk = devm_clk_get(&pdev->dev, NULL);
104e5279ff6SJulia Lawall if (IS_ERR(mdev->clk))
105e5279ff6SJulia Lawall return PTR_ERR(mdev->clk);
106a5fd9139SSascha Hauer
107955bc613SStefan Potyra err = clk_prepare_enable(mdev->clk);
108955bc613SStefan Potyra if (err)
109955bc613SStefan Potyra return err;
110955bc613SStefan Potyra
11171531f55SAlexander Shiyan clkrate = clk_get_rate(mdev->clk);
11271531f55SAlexander Shiyan if (clkrate < 10000000)
11371531f55SAlexander Shiyan dev_warn(&pdev->dev,
11471531f55SAlexander Shiyan "Low clock frequency causes improper function\n");
11571531f55SAlexander Shiyan
11671531f55SAlexander Shiyan clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000);
11771531f55SAlexander Shiyan clkrate /= clkdiv;
11871531f55SAlexander Shiyan if ((clkrate < 980000) || (clkrate > 1020000))
11971531f55SAlexander Shiyan dev_warn(&pdev->dev,
12071531f55SAlexander Shiyan "Incorrect time base frequency %lu Hz\n", clkrate);
121a5fd9139SSascha Hauer
122b0a523faSYueHaibing mdev->regs = devm_platform_ioremap_resource(pdev, 0);
123955bc613SStefan Potyra if (IS_ERR(mdev->regs)) {
124955bc613SStefan Potyra err = PTR_ERR(mdev->regs);
125955bc613SStefan Potyra goto out_disable_clk;
126955bc613SStefan Potyra }
127001d1953SAlexander Shiyan
128b7ce0b5dSAlexander Shiyan /* Software reset 1-Wire module */
129b7ce0b5dSAlexander Shiyan writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET);
130b7ce0b5dSAlexander Shiyan writeb(0, mdev->regs + MXC_W1_RESET);
131b7ce0b5dSAlexander Shiyan
132fc945d6eSAlexander Shiyan writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER);
133a5fd9139SSascha Hauer
134a5fd9139SSascha Hauer mdev->bus_master.data = mdev;
135a5fd9139SSascha Hauer mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus;
136a5fd9139SSascha Hauer mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit;
137a5fd9139SSascha Hauer
138a5fd9139SSascha Hauer platform_set_drvdata(pdev, mdev);
139001d1953SAlexander Shiyan
140001d1953SAlexander Shiyan err = w1_add_master_device(&mdev->bus_master);
141001d1953SAlexander Shiyan if (err)
142955bc613SStefan Potyra goto out_disable_clk;
143001d1953SAlexander Shiyan
144955bc613SStefan Potyra return 0;
145955bc613SStefan Potyra
146955bc613SStefan Potyra out_disable_clk:
147955bc613SStefan Potyra clk_disable_unprepare(mdev->clk);
148001d1953SAlexander Shiyan return err;
149a5fd9139SSascha Hauer }
150a5fd9139SSascha Hauer
151a5fd9139SSascha Hauer /*
152a5fd9139SSascha Hauer * disassociate the w1 device from the driver
153a5fd9139SSascha Hauer */
mxc_w1_remove(struct platform_device * pdev)15482849a93SBill Pemberton static int mxc_w1_remove(struct platform_device *pdev)
155a5fd9139SSascha Hauer {
156a5fd9139SSascha Hauer struct mxc_w1_device *mdev = platform_get_drvdata(pdev);
157a5fd9139SSascha Hauer
158a5fd9139SSascha Hauer w1_remove_master_device(&mdev->bus_master);
159a5fd9139SSascha Hauer
16060178b63SSascha Hauer clk_disable_unprepare(mdev->clk);
161a5fd9139SSascha Hauer
162a5fd9139SSascha Hauer return 0;
163a5fd9139SSascha Hauer }
164a5fd9139SSascha Hauer
1650a56c0e1SFabian Frederick static const struct of_device_id mxc_w1_dt_ids[] = {
16628c55dc1SMartin Fuzzey { .compatible = "fsl,imx21-owire" },
16728c55dc1SMartin Fuzzey { /* sentinel */ }
16828c55dc1SMartin Fuzzey };
16928c55dc1SMartin Fuzzey MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids);
17028c55dc1SMartin Fuzzey
171a5fd9139SSascha Hauer static struct platform_driver mxc_w1_driver = {
172a5fd9139SSascha Hauer .driver = {
173a5fd9139SSascha Hauer .name = "mxc_w1",
17428c55dc1SMartin Fuzzey .of_match_table = mxc_w1_dt_ids,
175a5fd9139SSascha Hauer },
176a5fd9139SSascha Hauer .probe = mxc_w1_probe,
17710532fe7SGreg Kroah-Hartman .remove = mxc_w1_remove,
178a5fd9139SSascha Hauer };
179fd21bfccSFabio Estevam module_platform_driver(mxc_w1_driver);
180a5fd9139SSascha Hauer
181a5fd9139SSascha Hauer MODULE_LICENSE("GPL");
182a5fd9139SSascha Hauer MODULE_AUTHOR("Freescale Semiconductors Inc");
183a5fd9139SSascha Hauer MODULE_DESCRIPTION("Driver for One-Wire on MXC");
184