xref: /openbmc/linux/drivers/w1/masters/mxc_w1.c (revision c9723750)
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