1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2012 Sven Schnelle <svens@stackframe.org> |
3 | |
4 | #include <linux/platform_device.h> |
5 | #include <linux/module.h> |
6 | #include <linux/init.h> |
7 | #include <linux/rtc.h> |
8 | #include <linux/types.h> |
9 | #include <linux/bcd.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <linux/io.h> |
15 | |
16 | #define DS2404_STATUS_REG 0x200 |
17 | #define DS2404_CONTROL_REG 0x201 |
18 | #define DS2404_RTC_REG 0x202 |
19 | |
20 | #define DS2404_WRITE_SCRATCHPAD_CMD 0x0f |
21 | #define DS2404_READ_SCRATCHPAD_CMD 0xaa |
22 | #define DS2404_COPY_SCRATCHPAD_CMD 0x55 |
23 | #define DS2404_READ_MEMORY_CMD 0xf0 |
24 | |
25 | #define DS2404_RST 0 |
26 | #define DS2404_CLK 1 |
27 | #define DS2404_DQ 2 |
28 | |
29 | struct ds2404 { |
30 | struct device *dev; |
31 | struct gpio_desc *rst_gpiod; |
32 | struct gpio_desc *clk_gpiod; |
33 | struct gpio_desc *dq_gpiod; |
34 | struct rtc_device *rtc; |
35 | }; |
36 | |
37 | static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev) |
38 | { |
39 | struct device *dev = &pdev->dev; |
40 | |
41 | /* This will de-assert RESET, declare this GPIO as GPIOD_ACTIVE_LOW */ |
42 | chip->rst_gpiod = devm_gpiod_get(dev, con_id: "rst" , flags: GPIOD_OUT_LOW); |
43 | if (IS_ERR(ptr: chip->rst_gpiod)) |
44 | return PTR_ERR(ptr: chip->rst_gpiod); |
45 | |
46 | chip->clk_gpiod = devm_gpiod_get(dev, con_id: "clk" , flags: GPIOD_OUT_HIGH); |
47 | if (IS_ERR(ptr: chip->clk_gpiod)) |
48 | return PTR_ERR(ptr: chip->clk_gpiod); |
49 | |
50 | chip->dq_gpiod = devm_gpiod_get(dev, con_id: "dq" , flags: GPIOD_ASIS); |
51 | if (IS_ERR(ptr: chip->dq_gpiod)) |
52 | return PTR_ERR(ptr: chip->dq_gpiod); |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static void ds2404_reset(struct ds2404 *chip) |
58 | { |
59 | gpiod_set_value(desc: chip->rst_gpiod, value: 1); |
60 | udelay(1000); |
61 | gpiod_set_value(desc: chip->rst_gpiod, value: 0); |
62 | gpiod_set_value(desc: chip->clk_gpiod, value: 0); |
63 | gpiod_direction_output(desc: chip->dq_gpiod, value: 0); |
64 | udelay(10); |
65 | } |
66 | |
67 | static void ds2404_write_byte(struct ds2404 *chip, u8 byte) |
68 | { |
69 | int i; |
70 | |
71 | gpiod_direction_output(desc: chip->dq_gpiod, value: 1); |
72 | for (i = 0; i < 8; i++) { |
73 | gpiod_set_value(desc: chip->dq_gpiod, value: byte & (1 << i)); |
74 | udelay(10); |
75 | gpiod_set_value(desc: chip->clk_gpiod, value: 1); |
76 | udelay(10); |
77 | gpiod_set_value(desc: chip->clk_gpiod, value: 0); |
78 | udelay(10); |
79 | } |
80 | } |
81 | |
82 | static u8 ds2404_read_byte(struct ds2404 *chip) |
83 | { |
84 | int i; |
85 | u8 ret = 0; |
86 | |
87 | gpiod_direction_input(desc: chip->dq_gpiod); |
88 | |
89 | for (i = 0; i < 8; i++) { |
90 | gpiod_set_value(desc: chip->clk_gpiod, value: 0); |
91 | udelay(10); |
92 | if (gpiod_get_value(desc: chip->dq_gpiod)) |
93 | ret |= 1 << i; |
94 | gpiod_set_value(desc: chip->clk_gpiod, value: 1); |
95 | udelay(10); |
96 | } |
97 | return ret; |
98 | } |
99 | |
100 | static void ds2404_read_memory(struct ds2404 *chip, u16 offset, |
101 | int length, u8 *out) |
102 | { |
103 | ds2404_reset(chip); |
104 | ds2404_write_byte(chip, DS2404_READ_MEMORY_CMD); |
105 | ds2404_write_byte(chip, byte: offset & 0xff); |
106 | ds2404_write_byte(chip, byte: (offset >> 8) & 0xff); |
107 | while (length--) |
108 | *out++ = ds2404_read_byte(chip); |
109 | } |
110 | |
111 | static void ds2404_write_memory(struct ds2404 *chip, u16 offset, |
112 | int length, u8 *out) |
113 | { |
114 | int i; |
115 | u8 ta01, ta02, es; |
116 | |
117 | ds2404_reset(chip); |
118 | ds2404_write_byte(chip, DS2404_WRITE_SCRATCHPAD_CMD); |
119 | ds2404_write_byte(chip, byte: offset & 0xff); |
120 | ds2404_write_byte(chip, byte: (offset >> 8) & 0xff); |
121 | |
122 | for (i = 0; i < length; i++) |
123 | ds2404_write_byte(chip, byte: out[i]); |
124 | |
125 | ds2404_reset(chip); |
126 | ds2404_write_byte(chip, DS2404_READ_SCRATCHPAD_CMD); |
127 | |
128 | ta01 = ds2404_read_byte(chip); |
129 | ta02 = ds2404_read_byte(chip); |
130 | es = ds2404_read_byte(chip); |
131 | |
132 | for (i = 0; i < length; i++) { |
133 | if (out[i] != ds2404_read_byte(chip)) { |
134 | dev_err(chip->dev, "read invalid data\n" ); |
135 | return; |
136 | } |
137 | } |
138 | |
139 | ds2404_reset(chip); |
140 | ds2404_write_byte(chip, DS2404_COPY_SCRATCHPAD_CMD); |
141 | ds2404_write_byte(chip, byte: ta01); |
142 | ds2404_write_byte(chip, byte: ta02); |
143 | ds2404_write_byte(chip, byte: es); |
144 | |
145 | while (gpiod_get_value(desc: chip->dq_gpiod)) |
146 | ; |
147 | } |
148 | |
149 | static void ds2404_enable_osc(struct ds2404 *chip) |
150 | { |
151 | u8 in[1] = { 0x10 }; /* enable oscillator */ |
152 | |
153 | ds2404_write_memory(chip, offset: 0x201, length: 1, out: in); |
154 | } |
155 | |
156 | static int ds2404_read_time(struct device *dev, struct rtc_time *dt) |
157 | { |
158 | struct ds2404 *chip = dev_get_drvdata(dev); |
159 | unsigned long time = 0; |
160 | __le32 hw_time = 0; |
161 | |
162 | ds2404_read_memory(chip, offset: 0x203, length: 4, out: (u8 *)&hw_time); |
163 | time = le32_to_cpu(hw_time); |
164 | |
165 | rtc_time64_to_tm(time, tm: dt); |
166 | return 0; |
167 | } |
168 | |
169 | static int ds2404_set_time(struct device *dev, struct rtc_time *dt) |
170 | { |
171 | struct ds2404 *chip = dev_get_drvdata(dev); |
172 | u32 time = cpu_to_le32(rtc_tm_to_time64(dt)); |
173 | ds2404_write_memory(chip, offset: 0x203, length: 4, out: (u8 *)&time); |
174 | return 0; |
175 | } |
176 | |
177 | static const struct rtc_class_ops ds2404_rtc_ops = { |
178 | .read_time = ds2404_read_time, |
179 | .set_time = ds2404_set_time, |
180 | }; |
181 | |
182 | static int rtc_probe(struct platform_device *pdev) |
183 | { |
184 | struct ds2404 *chip; |
185 | int retval = -EBUSY; |
186 | |
187 | chip = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct ds2404), GFP_KERNEL); |
188 | if (!chip) |
189 | return -ENOMEM; |
190 | |
191 | chip->dev = &pdev->dev; |
192 | |
193 | chip->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
194 | if (IS_ERR(ptr: chip->rtc)) |
195 | return PTR_ERR(ptr: chip->rtc); |
196 | |
197 | retval = ds2404_gpio_map(chip, pdev); |
198 | if (retval) |
199 | return retval; |
200 | |
201 | platform_set_drvdata(pdev, data: chip); |
202 | |
203 | chip->rtc->ops = &ds2404_rtc_ops; |
204 | chip->rtc->range_max = U32_MAX; |
205 | |
206 | retval = devm_rtc_register_device(chip->rtc); |
207 | if (retval) |
208 | return retval; |
209 | |
210 | ds2404_enable_osc(chip); |
211 | return 0; |
212 | } |
213 | |
214 | static struct platform_driver rtc_device_driver = { |
215 | .probe = rtc_probe, |
216 | .driver = { |
217 | .name = "ds2404" , |
218 | }, |
219 | }; |
220 | module_platform_driver(rtc_device_driver); |
221 | |
222 | MODULE_DESCRIPTION("DS2404 RTC" ); |
223 | MODULE_AUTHOR("Sven Schnelle" ); |
224 | MODULE_LICENSE("GPL" ); |
225 | MODULE_ALIAS("platform:ds2404" ); |
226 | |