aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-lp8841-rtc.c
diff options
context:
space:
mode:
authorSergei Ianovich <ynvich@gmail.com>2016-02-23 05:44:28 -0500
committerMark Brown <broonie@kernel.org>2016-02-23 23:09:08 -0500
commit7ecbfff6711fb331591003ac32c002ce55a0758f (patch)
tree12723faadcbc40a55e05c20ab90f7978b97d964a /drivers/spi/spi-lp8841-rtc.c
parent92e963f50fc74041b5e9e744c330dca48e04f08d (diff)
spi: master driver to enable RTC on ICPDAS LP-8841
ICP DAS LP-8841 contains a DS-1302 RTC. This driver provides an SPI master which makes the RTC usable. The driver is not supposed to work with anything else. The driver uses the standard MicroWire half-duplex transfer timing. Master output is set on low clock and sensed by the RTC on the rising edge. Master input is set by the RTC on the trailing edge and is sensed by the master on low clock. Signed-off-by: Sergei Ianovich <ynvich@gmail.com> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-lp8841-rtc.c')
-rw-r--r--drivers/spi/spi-lp8841-rtc.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c
new file mode 100644
index 000000000000..44bb69c3f1d6
--- /dev/null
+++ b/drivers/spi/spi-lp8841-rtc.c
@@ -0,0 +1,255 @@
1/*
2 * SPI master driver for ICP DAS LP-8841 RTC
3 *
4 * Copyright (C) 2016 Sergei Ianovich
5 *
6 * based on
7 *
8 * Dallas DS1302 RTC Support
9 * Copyright (C) 2002 David McCullough
10 * Copyright (C) 2003 - 2007 Paul Mundt
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22#include <linux/delay.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/platform_device.h>
26#include <linux/of.h>
27#include <linux/of_device.h>
28#include <linux/spi/spi.h>
29
30#define DRIVER_NAME "spi_lp8841_rtc"
31
32#define SPI_LP8841_RTC_CE 0x01
33#define SPI_LP8841_RTC_CLK 0x02
34#define SPI_LP8841_RTC_nWE 0x04
35#define SPI_LP8841_RTC_MOSI 0x08
36#define SPI_LP8841_RTC_MISO 0x01
37
38/*
39 * REVISIT If there is support for SPI_3WIRE and SPI_LSB_FIRST in SPI
40 * GPIO driver, this SPI driver can be replaced by a simple GPIO driver
41 * providing 3 GPIO pins.
42 */
43
44struct spi_lp8841_rtc {
45 void *iomem;
46 unsigned long state;
47};
48
49static inline void
50setsck(struct spi_lp8841_rtc *data, int is_on)
51{
52 if (is_on)
53 data->state |= SPI_LP8841_RTC_CLK;
54 else
55 data->state &= ~SPI_LP8841_RTC_CLK;
56 writeb(data->state, data->iomem);
57}
58
59static inline void
60setmosi(struct spi_lp8841_rtc *data, int is_on)
61{
62 if (is_on)
63 data->state |= SPI_LP8841_RTC_MOSI;
64 else
65 data->state &= ~SPI_LP8841_RTC_MOSI;
66 writeb(data->state, data->iomem);
67}
68
69static inline int
70getmiso(struct spi_lp8841_rtc *data)
71{
72 return ioread8(data->iomem) & SPI_LP8841_RTC_MISO;
73}
74
75static inline u32
76bitbang_txrx_be_cpha0_lsb(struct spi_lp8841_rtc *data,
77 unsigned usecs, unsigned cpol, unsigned flags,
78 u32 word, u8 bits)
79{
80 /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
81
82 u32 shift = 32 - bits;
83 /* clock starts at inactive polarity */
84 for (; likely(bits); bits--) {
85
86 /* setup LSB (to slave) on leading edge */
87 if ((flags & SPI_MASTER_NO_TX) == 0)
88 setmosi(data, (word & 1));
89
90 usleep_range(usecs, usecs + 1); /* T(setup) */
91
92 /* sample LSB (from slave) on trailing edge */
93 word >>= 1;
94 if ((flags & SPI_MASTER_NO_RX) == 0)
95 word |= (getmiso(data) << 31);
96
97 setsck(data, !cpol);
98 usleep_range(usecs, usecs + 1);
99
100 setsck(data, cpol);
101 }
102
103 word >>= shift;
104 return word;
105}
106
107static int
108spi_lp8841_rtc_transfer_one(struct spi_master *master,
109 struct spi_device *spi,
110 struct spi_transfer *t)
111{
112 struct spi_lp8841_rtc *data = spi_master_get_devdata(master);
113 unsigned count = t->len;
114 const u8 *tx = t->tx_buf;
115 u8 *rx = t->rx_buf;
116 u8 word = 0;
117 int ret = 0;
118
119 if (tx) {
120 data->state &= ~SPI_LP8841_RTC_nWE;
121 writeb(data->state, data->iomem);
122 while (likely(count > 0)) {
123 word = *tx++;
124 bitbang_txrx_be_cpha0_lsb(data, 1, 0,
125 SPI_MASTER_NO_RX, word, 8);
126 count--;
127 }
128 } else if (rx) {
129 data->state |= SPI_LP8841_RTC_nWE;
130 writeb(data->state, data->iomem);
131 while (likely(count > 0)) {
132 word = bitbang_txrx_be_cpha0_lsb(data, 1, 0,
133 SPI_MASTER_NO_TX, word, 8);
134 *rx++ = word;
135 count--;
136 }
137 } else {
138 ret = -EINVAL;
139 }
140
141 spi_finalize_current_transfer(master);
142
143 return ret;
144}
145
146static void
147spi_lp8841_rtc_set_cs(struct spi_device *spi, bool enable)
148{
149 struct spi_lp8841_rtc *data = spi_master_get_devdata(spi->master);
150
151 data->state = 0;
152 writeb(data->state, data->iomem);
153 if (enable) {
154 usleep_range(4, 5);
155 data->state |= SPI_LP8841_RTC_CE;
156 writeb(data->state, data->iomem);
157 usleep_range(4, 5);
158 }
159}
160
161static int
162spi_lp8841_rtc_setup(struct spi_device *spi)
163{
164 if ((spi->mode & SPI_CS_HIGH) == 0) {
165 dev_err(&spi->dev, "unsupported active low chip select\n");
166 return -EINVAL;
167 }
168
169 if ((spi->mode & SPI_LSB_FIRST) == 0) {
170 dev_err(&spi->dev, "unsupported MSB first mode\n");
171 return -EINVAL;
172 }
173
174 if ((spi->mode & SPI_3WIRE) == 0) {
175 dev_err(&spi->dev, "unsupported wiring. 3 wires required\n");
176 return -EINVAL;
177 }
178
179 return 0;
180}
181
182#ifdef CONFIG_OF
183static const struct of_device_id spi_lp8841_rtc_dt_ids[] = {
184 { .compatible = "icpdas,lp8841-spi-rtc" },
185 { }
186};
187
188MODULE_DEVICE_TABLE(of, spi_lp8841_rtc_dt_ids);
189#endif
190
191static int
192spi_lp8841_rtc_probe(struct platform_device *pdev)
193{
194 int ret;
195 struct spi_master *master;
196 struct spi_lp8841_rtc *data;
197 void *iomem;
198
199 master = spi_alloc_master(&pdev->dev, sizeof(*data));
200 if (!master)
201 return -ENOMEM;
202 platform_set_drvdata(pdev, master);
203
204 master->flags = SPI_MASTER_HALF_DUPLEX;
205 master->mode_bits = SPI_CS_HIGH | SPI_3WIRE | SPI_LSB_FIRST;
206
207 master->bus_num = pdev->id;
208 master->num_chipselect = 1;
209 master->setup = spi_lp8841_rtc_setup;
210 master->set_cs = spi_lp8841_rtc_set_cs;
211 master->transfer_one = spi_lp8841_rtc_transfer_one;
212 master->bits_per_word_mask = SPI_BPW_MASK(8);
213#ifdef CONFIG_OF
214 master->dev.of_node = pdev->dev.of_node;
215#endif
216
217 data = spi_master_get_devdata(master);
218
219 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
220 data->iomem = devm_ioremap_resource(&pdev->dev, iomem);
221 if (IS_ERR(data->iomem)) {
222 dev_err(&pdev->dev, "failed to get IO address\n");
223 goto err_put_master;
224 }
225
226 /* register with the SPI framework */
227 ret = devm_spi_register_master(&pdev->dev, master);
228 if (ret) {
229 dev_err(&pdev->dev, "cannot register spi master\n");
230 goto err_put_master;
231 }
232
233 return ret;
234
235
236err_put_master:
237 spi_master_put(master);
238
239 return ret;
240}
241
242MODULE_ALIAS("platform:" DRIVER_NAME);
243
244static struct platform_driver spi_lp8841_rtc_driver = {
245 .driver = {
246 .name = DRIVER_NAME,
247 .of_match_table = of_match_ptr(spi_lp8841_rtc_dt_ids),
248 },
249 .probe = spi_lp8841_rtc_probe,
250};
251module_platform_driver(spi_lp8841_rtc_driver);
252
253MODULE_DESCRIPTION("SPI master driver for ICP DAS LP-8841 RTC");
254MODULE_AUTHOR("Sergei Ianovich");
255MODULE_LICENSE("GPL");