diff options
Diffstat (limited to 'firmware/target/arm/s3c2440/i2c-s3c2440.c')
-rw-r--r-- | firmware/target/arm/s3c2440/i2c-s3c2440.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/firmware/target/arm/s3c2440/i2c-s3c2440.c b/firmware/target/arm/s3c2440/i2c-s3c2440.c new file mode 100644 index 0000000000..4669186a4c --- /dev/null +++ b/firmware/target/arm/s3c2440/i2c-s3c2440.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "system.h" | ||
22 | #include "i2c-s3c2440.h" | ||
23 | |||
24 | static struct wakeup i2c_wake; /* Transfer completion signal */ | ||
25 | static struct mutex i2c_mtx; /* Mutual exclusion */ | ||
26 | static unsigned char *buf_ptr; /* Next byte to transfer */ | ||
27 | static int buf_count; /* Number of bytes remaining to transfer */ | ||
28 | |||
29 | static void i2c_stop(void) | ||
30 | { | ||
31 | /* Generate STOP */ | ||
32 | IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB; | ||
33 | |||
34 | /* No more interrupts, clear pending interrupt to continue */ | ||
35 | IICCON &= ~(I2C_TXRX_INTPND | I2C_TXRX_INTENB); | ||
36 | } | ||
37 | |||
38 | void i2c_write(int addr, const unsigned char *buf, int count) | ||
39 | { | ||
40 | if (count <= 0) | ||
41 | return; | ||
42 | |||
43 | mutex_lock(&i2c_mtx); | ||
44 | |||
45 | /* Turn on I2C clock */ | ||
46 | s3c_regset32(&CLKCON, 1 << 16); | ||
47 | |||
48 | /* Set mode to master transmitter and enable lines */ | ||
49 | IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_RXTX_ENB; | ||
50 | |||
51 | /* Set buffer start and count */ | ||
52 | buf_ptr = (unsigned char *)buf; | ||
53 | buf_count = count; | ||
54 | |||
55 | /* Send slave address and then data */ | ||
56 | SRCPND = IIC_MASK; | ||
57 | INTPND = IIC_MASK; | ||
58 | |||
59 | IICCON |= I2C_TXRX_INTENB; | ||
60 | |||
61 | /* Load slave address into shift register */ | ||
62 | IICDS = addr & 0xfe; | ||
63 | |||
64 | /* Generate START */ | ||
65 | IICSTAT = I2C_MODE_MASTER | I2C_MODE_TX | I2C_START | I2C_RXTX_ENB; | ||
66 | |||
67 | if (wakeup_wait(&i2c_wake, HZ) != OBJ_WAIT_SUCCEEDED) | ||
68 | { | ||
69 | /* Something went wrong - stop transmission */ | ||
70 | int oldlevel = disable_irq_save(); | ||
71 | i2c_stop(); | ||
72 | restore_irq(oldlevel); | ||
73 | } | ||
74 | |||
75 | /* Go back to slave receive mode and disable lines */ | ||
76 | IICSTAT = 0; | ||
77 | |||
78 | /* Turn off I2C clock */ | ||
79 | s3c_regclr32(&CLKCON, 1 << 16); | ||
80 | |||
81 | mutex_unlock(&i2c_mtx); | ||
82 | } | ||
83 | |||
84 | void i2c_init(void) | ||
85 | { | ||
86 | /* Init kernel objects */ | ||
87 | wakeup_init(&i2c_wake); | ||
88 | mutex_init(&i2c_mtx); | ||
89 | |||
90 | /* Clear pending source */ | ||
91 | SRCPND = IIC_MASK; | ||
92 | INTPND = IIC_MASK; | ||
93 | |||
94 | /* Enable i2c interrupt in controller */ | ||
95 | s3c_regclr32(&INTMOD, IIC_MASK); | ||
96 | s3c_regclr32(&INTMSK, IIC_MASK); | ||
97 | |||
98 | /* Turn on I2C clock */ | ||
99 | s3c_regset32(&CLKCON, 1 << 16); | ||
100 | |||
101 | /* Set GPE15 (IICSDA) and GPE14 (IICSCL) to IIC */ | ||
102 | GPECON = (GPECON & ~((3 << 30) | (3 << 28))) | | ||
103 | ((2 << 30) | (2 << 28)); | ||
104 | |||
105 | /* Bus ACK, IICCLK: fPCLK / 16, Rx/Tx Int: Disable, Tx clock: IICCLK/8 */ | ||
106 | /* OF PCLK: 49.1568MHz / 16 / 8 = 384.0375 kHz */ | ||
107 | IICCON = (7 << 0); | ||
108 | |||
109 | /* SDA line delayed 0 PCLKs */ | ||
110 | IICLC = (0 << 0); | ||
111 | |||
112 | /* Turn off I2C clock */ | ||
113 | s3c_regclr32(&CLKCON, 1 << 16); | ||
114 | } | ||
115 | |||
116 | void IIC(void) | ||
117 | { | ||
118 | for (;;) | ||
119 | { | ||
120 | /* If ack was received from last byte and bytes are remaining */ | ||
121 | if (--buf_count >= 0 && (IICSTAT & I2C_ACK_L) == 0) | ||
122 | { | ||
123 | /* Write next byte to shift register */ | ||
124 | IICDS = *buf_ptr++; | ||
125 | |||
126 | /* Clear pending interrupt to continue */ | ||
127 | IICCON &= ~I2C_TXRX_INTPND; | ||
128 | break; | ||
129 | } | ||
130 | |||
131 | /* Finished */ | ||
132 | |||
133 | /* Generate STOP */ | ||
134 | i2c_stop(); | ||
135 | |||
136 | /* Signal thread */ | ||
137 | wakeup_signal(&i2c_wake); | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | /* Ack */ | ||
142 | SRCPND = IIC_MASK; | ||
143 | INTPND = IIC_MASK; | ||
144 | } | ||