aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorCyril Chemparathy <cyril@ti.com>2011-01-18 14:21:35 -0500
committerKevin Hilman <khilman@ti.com>2011-03-15 11:17:22 -0400
commit03df0f629483f941f83ee2fbb180a4425e99dd23 (patch)
tree3e7ec2e38067799b5a79ea273057bd5e180c9504 /drivers
parent9a9fb12a4832bdf22751e21df298ef3559643b43 (diff)
mfd: add driver for sequencer serial port
TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port device. It has a built-in programmable execution engine that can be programmed to operate as almost any serial bus (I2C, SPI, EasyScale, and others). This patch adds a driver for this controller device. The driver does not expose a user-land interface. Protocol drivers built on top of this layer are expected to remain in-kernel. Signed-off-by: Cyril Chemparathy <cyril@ti.com> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Sekhar Nori <nsekhar@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig11
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/ti-ssp.c476
3 files changed, 488 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fd018366d670..0284c53c210c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -81,6 +81,17 @@ config MFD_DM355EVM_MSP
81 boards. MSP430 firmware manages resets and power sequencing, 81 boards. MSP430 firmware manages resets and power sequencing,
82 inputs from buttons and the IR remote, LEDs, an RTC, and more. 82 inputs from buttons and the IR remote, LEDs, an RTC, and more.
83 83
84config MFD_TI_SSP
85 tristate "TI Sequencer Serial Port support"
86 depends on ARCH_DAVINCI_TNETV107X
87 select MFD_CORE
88 ---help---
89 Say Y here if you want support for the Sequencer Serial Port
90 in a Texas Instruments TNETV107X SoC.
91
92 To compile this driver as a module, choose M here: the
93 module will be called ti-ssp.
94
84config HTC_EGPIO 95config HTC_EGPIO
85 bool "HTC EGPIO support" 96 bool "HTC EGPIO support"
86 depends on GENERIC_HARDIRQS && GPIOLIB && ARM 97 depends on GENERIC_HARDIRQS && GPIOLIB && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a54e2c7c6a1c..c56b6c7232ed 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
14 14
15obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o 15obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
16obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o 16obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
17obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
17 18
18obj-$(CONFIG_MFD_STMPE) += stmpe.o 19obj-$(CONFIG_MFD_STMPE) += stmpe.o
19obj-$(CONFIG_MFD_TC3589X) += tc3589x.o 20obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c
new file mode 100644
index 000000000000..af9ab0e5ca64
--- /dev/null
+++ b/drivers/mfd/ti-ssp.c
@@ -0,0 +1,476 @@
1/*
2 * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs
3 *
4 * Copyright (C) 2010 Texas Instruments Inc
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <linux/errno.h>
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/slab.h>
25#include <linux/err.h>
26#include <linux/init.h>
27#include <linux/wait.h>
28#include <linux/clk.h>
29#include <linux/interrupt.h>
30#include <linux/device.h>
31#include <linux/spinlock.h>
32#include <linux/platform_device.h>
33#include <linux/delay.h>
34#include <linux/io.h>
35#include <linux/mfd/core.h>
36#include <linux/mfd/ti_ssp.h>
37
38/* Register Offsets */
39#define REG_REV 0x00
40#define REG_IOSEL_1 0x04
41#define REG_IOSEL_2 0x08
42#define REG_PREDIV 0x0c
43#define REG_INTR_ST 0x10
44#define REG_INTR_EN 0x14
45#define REG_TEST_CTRL 0x18
46
47/* Per port registers */
48#define PORT_CFG_2 0x00
49#define PORT_ADDR 0x04
50#define PORT_DATA 0x08
51#define PORT_CFG_1 0x0c
52#define PORT_STATE 0x10
53
54#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT)
55#define SSP_PORT_CLKRATE_MASK 0x0f
56
57#define SSP_SEQRAM_WR_EN BIT(4)
58#define SSP_SEQRAM_RD_EN BIT(5)
59#define SSP_START BIT(15)
60#define SSP_BUSY BIT(10)
61#define SSP_PORT_ASL BIT(7)
62#define SSP_PORT_CFO1 BIT(6)
63
64#define SSP_PORT_SEQRAM_SIZE 32
65
66static const int ssp_port_base[] = {0x040, 0x080};
67static const int ssp_port_seqram[] = {0x100, 0x180};
68
69struct ti_ssp {
70 struct resource *res;
71 struct device *dev;
72 void __iomem *regs;
73 spinlock_t lock;
74 struct clk *clk;
75 int irq;
76 wait_queue_head_t wqh;
77
78 /*
79 * Some of the iosel2 register bits always read-back as 0, we need to
80 * remember these values so that we don't clobber previously set
81 * values.
82 */
83 u32 iosel2;
84};
85
86static inline struct ti_ssp *dev_to_ssp(struct device *dev)
87{
88 return dev_get_drvdata(dev->parent);
89}
90
91static inline int dev_to_port(struct device *dev)
92{
93 return to_platform_device(dev)->id;
94}
95
96/* Register Access Helpers, rmw() functions need to run locked */
97static inline u32 ssp_read(struct ti_ssp *ssp, int reg)
98{
99 return __raw_readl(ssp->regs + reg);
100}
101
102static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val)
103{
104 __raw_writel(val, ssp->regs + reg);
105}
106
107static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits)
108{
109 ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits);
110}
111
112static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg)
113{
114 return ssp_read(ssp, ssp_port_base[port] + reg);
115}
116
117static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg,
118 u32 val)
119{
120 ssp_write(ssp, ssp_port_base[port] + reg, val);
121}
122
123static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg,
124 u32 mask, u32 bits)
125{
126 ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits);
127}
128
129static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg,
130 u32 bits)
131{
132 ssp_port_rmw(ssp, port, reg, bits, 0);
133}
134
135static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg,
136 u32 bits)
137{
138 ssp_port_rmw(ssp, port, reg, 0, bits);
139}
140
141/* Called to setup port clock mode, caller must hold ssp->lock */
142static int __set_mode(struct ti_ssp *ssp, int port, int mode)
143{
144 mode &= SSP_PORT_CONFIG_MASK;
145 ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode);
146
147 return 0;
148}
149
150int ti_ssp_set_mode(struct device *dev, int mode)
151{
152 struct ti_ssp *ssp = dev_to_ssp(dev);
153 int port = dev_to_port(dev);
154 int ret;
155
156 spin_lock(&ssp->lock);
157 ret = __set_mode(ssp, port, mode);
158 spin_unlock(&ssp->lock);
159
160 return ret;
161}
162EXPORT_SYMBOL(ti_ssp_set_mode);
163
164/* Called to setup iosel2, caller must hold ssp->lock */
165static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val)
166{
167 ssp->iosel2 = (ssp->iosel2 & ~mask) | val;
168 ssp_write(ssp, REG_IOSEL_2, ssp->iosel2);
169}
170
171/* Called to setup port iosel, caller must hold ssp->lock */
172static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel)
173{
174 unsigned val, shift = port ? 16 : 0;
175
176 /* IOSEL1 gets the least significant 16 bits */
177 val = ssp_read(ssp, REG_IOSEL_1);
178 val &= 0xffff << (port ? 0 : 16);
179 val |= (iosel & 0xffff) << (port ? 16 : 0);
180 ssp_write(ssp, REG_IOSEL_1, val);
181
182 /* IOSEL2 gets the most significant 16 bits */
183 val = (iosel >> 16) & 0x7;
184 __set_iosel2(ssp, 0x7 << shift, val << shift);
185}
186
187int ti_ssp_set_iosel(struct device *dev, u32 iosel)
188{
189 struct ti_ssp *ssp = dev_to_ssp(dev);
190 int port = dev_to_port(dev);
191
192 spin_lock(&ssp->lock);
193 __set_iosel(ssp, port, iosel);
194 spin_unlock(&ssp->lock);
195
196 return 0;
197}
198EXPORT_SYMBOL(ti_ssp_set_iosel);
199
200int ti_ssp_load(struct device *dev, int offs, u32* prog, int len)
201{
202 struct ti_ssp *ssp = dev_to_ssp(dev);
203 int port = dev_to_port(dev);
204 int i;
205
206 if (len > SSP_PORT_SEQRAM_SIZE)
207 return -ENOSPC;
208
209 spin_lock(&ssp->lock);
210
211 /* Enable SeqRAM access */
212 ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
213
214 /* Copy code */
215 for (i = 0; i < len; i++) {
216 __raw_writel(prog[i], ssp->regs + offs + 4*i +
217 ssp_port_seqram[port]);
218 }
219
220 /* Disable SeqRAM access */
221 ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
222
223 spin_unlock(&ssp->lock);
224
225 return 0;
226}
227EXPORT_SYMBOL(ti_ssp_load);
228
229int ti_ssp_raw_read(struct device *dev)
230{
231 struct ti_ssp *ssp = dev_to_ssp(dev);
232 int port = dev_to_port(dev);
233 int shift = port ? 27 : 11;
234
235 return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf;
236}
237EXPORT_SYMBOL(ti_ssp_raw_read);
238
239int ti_ssp_raw_write(struct device *dev, u32 val)
240{
241 struct ti_ssp *ssp = dev_to_ssp(dev);
242 int port = dev_to_port(dev), shift;
243
244 spin_lock(&ssp->lock);
245
246 shift = port ? 22 : 6;
247 val &= 0xf;
248 __set_iosel2(ssp, 0xf << shift, val << shift);
249
250 spin_unlock(&ssp->lock);
251
252 return 0;
253}
254EXPORT_SYMBOL(ti_ssp_raw_write);
255
256static inline int __xfer_done(struct ti_ssp *ssp, int port)
257{
258 return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY);
259}
260
261int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output)
262{
263 struct ti_ssp *ssp = dev_to_ssp(dev);
264 int port = dev_to_port(dev);
265 int ret;
266
267 if (pc & ~(0x3f))
268 return -EINVAL;
269
270 /* Grab ssp->lock to serialize rmw on ssp registers */
271 spin_lock(&ssp->lock);
272
273 ssp_port_write(ssp, port, PORT_ADDR, input >> 16);
274 ssp_port_write(ssp, port, PORT_DATA, input & 0xffff);
275 ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc);
276
277 /* grab wait queue head lock to avoid race with the isr */
278 spin_lock_irq(&ssp->wqh.lock);
279
280 /* kick off sequence execution in hardware */
281 ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START);
282
283 /* drop ssp lock; no register writes beyond this */
284 spin_unlock(&ssp->lock);
285
286 ret = wait_event_interruptible_locked_irq(ssp->wqh,
287 __xfer_done(ssp, port));
288 spin_unlock_irq(&ssp->wqh.lock);
289
290 if (ret < 0)
291 return ret;
292
293 if (output) {
294 *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) |
295 (ssp_port_read(ssp, port, PORT_DATA) & 0xffff);
296 }
297
298 ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */
299
300 return ret;
301}
302EXPORT_SYMBOL(ti_ssp_run);
303
304static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data)
305{
306 struct ti_ssp *ssp = dev_data;
307
308 spin_lock(&ssp->wqh.lock);
309
310 ssp_write(ssp, REG_INTR_ST, 0x3);
311 wake_up_locked(&ssp->wqh);
312
313 spin_unlock(&ssp->wqh.lock);
314
315 return IRQ_HANDLED;
316}
317
318static int __devinit ti_ssp_probe(struct platform_device *pdev)
319{
320 static struct ti_ssp *ssp;
321 const struct ti_ssp_data *pdata = pdev->dev.platform_data;
322 int error = 0, prediv = 0xff, id;
323 unsigned long sysclk;
324 struct device *dev = &pdev->dev;
325 struct mfd_cell cells[2];
326
327 ssp = kzalloc(sizeof(*ssp), GFP_KERNEL);
328 if (!ssp) {
329 dev_err(dev, "cannot allocate device info\n");
330 return -ENOMEM;
331 }
332
333 ssp->dev = dev;
334 dev_set_drvdata(dev, ssp);
335
336 ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
337 if (!ssp->res) {
338 error = -ENODEV;
339 dev_err(dev, "cannot determine register area\n");
340 goto error_res;
341 }
342
343 if (!request_mem_region(ssp->res->start, resource_size(ssp->res),
344 pdev->name)) {
345 error = -ENOMEM;
346 dev_err(dev, "cannot claim register memory\n");
347 goto error_res;
348 }
349
350 ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res));
351 if (!ssp->regs) {
352 error = -ENOMEM;
353 dev_err(dev, "cannot map register memory\n");
354 goto error_map;
355 }
356
357 ssp->clk = clk_get(dev, NULL);
358 if (IS_ERR(ssp->clk)) {
359 error = PTR_ERR(ssp->clk);
360 dev_err(dev, "cannot claim device clock\n");
361 goto error_clk;
362 }
363
364 ssp->irq = platform_get_irq(pdev, 0);
365 if (ssp->irq < 0) {
366 error = -ENODEV;
367 dev_err(dev, "unknown irq\n");
368 goto error_irq;
369 }
370
371 error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0,
372 dev_name(dev), ssp);
373 if (error < 0) {
374 dev_err(dev, "cannot acquire irq\n");
375 goto error_irq;
376 }
377
378 spin_lock_init(&ssp->lock);
379 init_waitqueue_head(&ssp->wqh);
380
381 /* Power on and initialize SSP */
382 error = clk_enable(ssp->clk);
383 if (error) {
384 dev_err(dev, "cannot enable device clock\n");
385 goto error_enable;
386 }
387
388 /* Reset registers to a sensible known state */
389 ssp_write(ssp, REG_IOSEL_1, 0);
390 ssp_write(ssp, REG_IOSEL_2, 0);
391 ssp_write(ssp, REG_INTR_EN, 0x3);
392 ssp_write(ssp, REG_INTR_ST, 0x3);
393 ssp_write(ssp, REG_TEST_CTRL, 0);
394 ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL);
395 ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL);
396 ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1);
397 ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1);
398
399 sysclk = clk_get_rate(ssp->clk);
400 if (pdata && pdata->out_clock)
401 prediv = (sysclk / pdata->out_clock) - 1;
402 prediv = clamp(prediv, 0, 0xff);
403 ssp_rmw(ssp, REG_PREDIV, 0xff, prediv);
404
405 memset(cells, 0, sizeof(cells));
406 for (id = 0; id < 2; id++) {
407 const struct ti_ssp_dev_data *data = &pdata->dev_data[id];
408
409 cells[id].id = id;
410 cells[id].name = data->dev_name;
411 cells[id].platform_data = data->pdata;
412 cells[id].data_size = data->pdata_size;
413 }
414
415 error = mfd_add_devices(dev, 0, cells, 2, NULL, 0);
416 if (error < 0) {
417 dev_err(dev, "cannot add mfd cells\n");
418 goto error_enable;
419 }
420
421 return 0;
422
423error_enable:
424 free_irq(ssp->irq, ssp);
425error_irq:
426 clk_put(ssp->clk);
427error_clk:
428 iounmap(ssp->regs);
429error_map:
430 release_mem_region(ssp->res->start, resource_size(ssp->res));
431error_res:
432 kfree(ssp);
433 return error;
434}
435
436static int __devexit ti_ssp_remove(struct platform_device *pdev)
437{
438 struct device *dev = &pdev->dev;
439 struct ti_ssp *ssp = dev_get_drvdata(dev);
440
441 mfd_remove_devices(dev);
442 clk_disable(ssp->clk);
443 free_irq(ssp->irq, ssp);
444 clk_put(ssp->clk);
445 iounmap(ssp->regs);
446 release_mem_region(ssp->res->start, resource_size(ssp->res));
447 kfree(ssp);
448 dev_set_drvdata(dev, NULL);
449 return 0;
450}
451
452static struct platform_driver ti_ssp_driver = {
453 .probe = ti_ssp_probe,
454 .remove = __devexit_p(ti_ssp_remove),
455 .driver = {
456 .name = "ti-ssp",
457 .owner = THIS_MODULE,
458 }
459};
460
461static int __init ti_ssp_init(void)
462{
463 return platform_driver_register(&ti_ssp_driver);
464}
465module_init(ti_ssp_init);
466
467static void __exit ti_ssp_exit(void)
468{
469 platform_driver_unregister(&ti_ssp_driver);
470}
471module_exit(ti_ssp_exit);
472
473MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
474MODULE_AUTHOR("Cyril Chemparathy");
475MODULE_LICENSE("GPL");
476MODULE_ALIAS("platform:ti-ssp");