Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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.
13 */
14
15#include <linux/clk.h>
16#include <linux/delay.h>
17#include <linux/io.h>
18#include <linux/module.h>
19#include <linux/platform_device.h>
20
21#include "../w1.h"
22#include "../w1_int.h"
23
24/* According to the mx27 Datasheet the reset procedure should take up to about
25 * 1350us. We set the timeout to 500*100us = 50ms for sure */
26#define MXC_W1_RESET_TIMEOUT 500
27
28/*
29 * MXC W1 Register offsets
30 */
31#define MXC_W1_CONTROL 0x00
32# define MXC_W1_CONTROL_RDST BIT(3)
33# define MXC_W1_CONTROL_WR(x) BIT(5 - (x))
34# define MXC_W1_CONTROL_PST BIT(6)
35# define MXC_W1_CONTROL_RPP BIT(7)
36#define MXC_W1_TIME_DIVIDER 0x02
37#define MXC_W1_RESET 0x04
38
39struct mxc_w1_device {
40 void __iomem *regs;
41 struct clk *clk;
42 struct w1_bus_master bus_master;
43};
44
45/*
46 * this is the low level routine to
47 * reset the device on the One Wire interface
48 * on the hardware
49 */
50static u8 mxc_w1_ds2_reset_bus(void *data)
51{
52 u8 reg_val;
53 unsigned int timeout_cnt = 0;
54 struct mxc_w1_device *dev = data;
55
56 writeb(MXC_W1_CONTROL_RPP, (dev->regs + MXC_W1_CONTROL));
57
58 while (1) {
59 reg_val = readb(dev->regs + MXC_W1_CONTROL);
60
61 if (!(reg_val & MXC_W1_CONTROL_RPP) ||
62 timeout_cnt > MXC_W1_RESET_TIMEOUT)
63 break;
64 else
65 timeout_cnt++;
66
67 udelay(100);
68 }
69 return !!(reg_val & MXC_W1_CONTROL_PST);
70}
71
72/*
73 * this is the low level routine to read/write a bit on the One Wire
74 * interface on the hardware. It does write 0 if parameter bit is set
75 * to 0, otherwise a write 1/read.
76 */
77static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
78{
79 struct mxc_w1_device *mdev = data;
80 void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL;
81 unsigned int timeout_cnt = 400; /* Takes max. 120us according to
82 * datasheet.
83 */
84
85 writeb(MXC_W1_CONTROL_WR(bit), ctrl_addr);
86
87 while (timeout_cnt--) {
88 if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit)))
89 break;
90
91 udelay(1);
92 }
93
94 return !!(readb(ctrl_addr) & MXC_W1_CONTROL_RDST);
95}
96
97static int mxc_w1_probe(struct platform_device *pdev)
98{
99 struct mxc_w1_device *mdev;
100 unsigned long clkrate;
101 struct resource *res;
102 unsigned int clkdiv;
103 int err;
104
105 mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device),
106 GFP_KERNEL);
107 if (!mdev)
108 return -ENOMEM;
109
110 mdev->clk = devm_clk_get(&pdev->dev, NULL);
111 if (IS_ERR(mdev->clk))
112 return PTR_ERR(mdev->clk);
113
114 clkrate = clk_get_rate(mdev->clk);
115 if (clkrate < 10000000)
116 dev_warn(&pdev->dev,
117 "Low clock frequency causes improper function\n");
118
119 clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000);
120 clkrate /= clkdiv;
121 if ((clkrate < 980000) || (clkrate > 1020000))
122 dev_warn(&pdev->dev,
123 "Incorrect time base frequency %lu Hz\n", clkrate);
124
125 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
126 mdev->regs = devm_ioremap_resource(&pdev->dev, res);
127 if (IS_ERR(mdev->regs))
128 return PTR_ERR(mdev->regs);
129
130 err = clk_prepare_enable(mdev->clk);
131 if (err)
132 return err;
133
134 writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER);
135
136 mdev->bus_master.data = mdev;
137 mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus;
138 mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit;
139
140 platform_set_drvdata(pdev, mdev);
141
142 err = w1_add_master_device(&mdev->bus_master);
143 if (err)
144 clk_disable_unprepare(mdev->clk);
145
146 return err;
147}
148
149/*
150 * disassociate the w1 device from the driver
151 */
152static int mxc_w1_remove(struct platform_device *pdev)
153{
154 struct mxc_w1_device *mdev = platform_get_drvdata(pdev);
155
156 w1_remove_master_device(&mdev->bus_master);
157
158 clk_disable_unprepare(mdev->clk);
159
160 return 0;
161}
162
163static struct of_device_id mxc_w1_dt_ids[] = {
164 { .compatible = "fsl,imx21-owire" },
165 { /* sentinel */ }
166};
167MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids);
168
169static struct platform_driver mxc_w1_driver = {
170 .driver = {
171 .name = "mxc_w1",
172 .owner = THIS_MODULE,
173 .of_match_table = mxc_w1_dt_ids,
174 },
175 .probe = mxc_w1_probe,
176 .remove = mxc_w1_remove,
177};
178module_platform_driver(mxc_w1_driver);
179
180MODULE_LICENSE("GPL");
181MODULE_AUTHOR("Freescale Semiconductors Inc");
182MODULE_DESCRIPTION("Driver for One-Wire on MXC");