1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
/*
* Copyright (C) 2007 Sascha Hauer, Pengutronix
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <common.h>
#include <init.h>
#include <driver.h>
#include <spi/spi.h>
#include <xfuncs.h>
#include <errno.h>
#include <asm/arch/pmic.h>
#define REG_INTERRUPT_STATUS_0 0x0
#define REG_INTERRUPT_MASK 0x1
#define REG_INTERRUPT_SENSE_0 0x2
#define REG_INTERRUPT_STATUS_1 0x3
#define REG_INTERRUPT_MASK_1 0x4
#define REG_INTERRUPT_SENSE_1 0x5
#define REG_POWER_UP_MODE_SENSE 0x6
#define REG_REVISION 0x7
#define REG_SEMAPHORE 0x8
#define REG_ARBITRATION_PERIPHERAL_AUDIO 0x9
#define REG_ARBITRATION_SWITCHERS 0xa
#define REG_ARBITRATION_REGULATORS(x) (0xb + (x)) /* 0 .. 1 */
#define REG_POWER_CONTROL(x) (0xd + (x)) /* 0 .. 2 */
#define REG_REGEN_ASSIGNMENT 0x10
#define REG_CONTROL_SPARE 0x11
#define REG_MEMORY_A 0x12
#define REG_MEMORY_B 0x13
#define REG_RTC_TIME 0x14
#define REG_RTC_ALARM 0x15
#define REG_RTC_DAY 0x16
#define REG_RTC_DAY_ALARM 0x17
#define REG_SWITCHERS(x) (0x18 + (x)) /* 0 .. 5 */
#define REG_REGULATOR_SETTING(x) (0x1e + (x)) /* 0 .. 1 */
#define REG_REGULATOR_MODE(x) (0x20 + (x)) /* 0 .. 1 */
#define REG_POWER_MISCELLANEOUS 0x22
#define REG_POWER_SPARE 0x23
#define REG_AUDIO_RX_0 0x24
#define REG_AUDIO_RX_1 0x25
#define REG_AUDIO_TX 0x26
#define REG_AUDIO_SSI_NETWORK 0x27
#define REG_AUDIO_CODEC 0x28
#define REG_AUDIO_STEREO_DAC 0x29
#define REG_AUDIO_SPARE 0x2a
#define REG_ADC(x) (0x2b + (x)) /* 0 .. 4 */
#define REG_CHARGER 0x30
#define REG_USB 0x31
#define REG_CHARGE_USB_SPARE 0x32
#define REG_LED_CONTROL(x) (0x33 + (x)) /* 0 .. 5 */
#define REG_SPARE 0x39
#define REG_TRIM(x) (0x3a + (x)) /* 0 .. 1 */
#define REG_TEST(x) (0x3c + (x)) /* 0 .. 3 */
struct spi_device *pmic_spi;
#define MXC_PMIC_REG_NUM(reg) (((reg) & 0x3f) << 25)
#define MXC_PMIC_WRITE (1 << 31)
#define SWX_VOLTAGE(x) ((x) & 0x3f)
#define SWX_VOLTAGE_DVS(x) (((x) & 0x3f) << 6)
#define SWX_VOLTAGE_STANDBY(x) (((x) & 0x3f) << 12)
#define SWX_VOLTAGE_1_450 0x16
#define SWX_MODE_OFF 0
#define SWX_MODE_NO_PULSE_SKIP 1
#define SWX_MODE_PULSE_SKIP 2
#define SWX_MODE_LOW_POWER_PFM 3
#define SW1A_MODE(x) (((x) & 0x3) << 0)
#define SW1A_MODE_STANDBY(x) (((x) & 0x3) << 2)
#define SW1B_MODE(x) (((x) & 0x3) << 10)
#define SW1B_MODE_STANDBY(x) (((x) & 0x3) << 12)
#define SW1A_SOFTSTART (1 << 9)
#define SW1B_SOFTSTART (1 << 17)
#define SW_PLL_FACTOR(x) (((x) - 28) << 19)
static int spi_rw(struct spi_device *spi, void * buf, size_t len)
{
int ret;
struct spi_transfer t = {
.tx_buf = (const void *)buf,
.rx_buf = buf,
.len = len,
.cs_change = 0,
.delay_usecs = 0,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
if ((ret = spi_sync(spi, &m)))
return ret;
return 0;
}
static uint32_t pmic_read_reg(struct spi_device *spi, int reg)
{
uint32_t buf;
buf = MXC_PMIC_REG_NUM(reg);
/* Need to read twice here, as seen in redboot code.
* FIXME: Is this a pmic bug or a bug in the spi
* controller?
*/
spi_rw(pmic_spi, &buf, 4);
spi_rw(pmic_spi, &buf, 4);
return buf;
}
static void pmic_write_reg(struct spi_device *spi, int reg, uint32_t val)
{
uint32_t buf = MXC_PMIC_REG_NUM(reg) | MXC_PMIC_WRITE | (val & 0xffffff);
spi_rw(pmic_spi, &buf, 4);
}
int pmic_power(void)
{
if(!pmic_spi) {
printf("%s: no pmic device available\n", __FUNCTION__);
return -ENODEV;
}
pmic_write_reg(pmic_spi, REG_SWITCHERS(0),
SWX_VOLTAGE(SWX_VOLTAGE_1_450) |
SWX_VOLTAGE_DVS(SWX_VOLTAGE_1_450) |
SWX_VOLTAGE_STANDBY(SWX_VOLTAGE_1_450));
pmic_write_reg(pmic_spi, REG_SWITCHERS(4),
SW1A_MODE(SWX_MODE_NO_PULSE_SKIP) |
SW1A_MODE_STANDBY(SWX_MODE_NO_PULSE_SKIP)|
SW1A_SOFTSTART |
SW1B_MODE(SWX_MODE_NO_PULSE_SKIP) |
SW1B_MODE_STANDBY(SWX_MODE_NO_PULSE_SKIP) |
SW1B_SOFTSTART |
SW_PLL_FACTOR(32)
);
return 0;
}
ssize_t pmic_read(struct device_d *dev, void *_buf, size_t count, ulong offset, ulong flags)
{
int i = count >> 2;
uint32_t *buf = _buf;
offset >>= 2;
while (i) {
*buf = pmic_read_reg(pmic_spi, offset);
buf++;
i--;
offset++;
}
return count;
}
ssize_t pmic_write(struct device_d *dev, const void *_buf, size_t count, ulong offset, ulong flags)
{
int i = count >> 2;
const uint32_t *buf = _buf;
offset >>= 2;
while (i) {
pmic_write_reg(pmic_spi, offset, *buf);
buf++;
i--;
offset++;
}
return count;
}
static int pmic_probe(struct device_d *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
dev->size = 256;
spi->mode = SPI_MODE_2 | SPI_CS_HIGH;
spi->bits_per_word = 32;
pmic_spi = spi;
return 0;
}
static struct file_operations pmic_fops = {
.open = dev_open_default,
.lseek = dev_lseek_default,
.close = dev_close_default,
.read = pmic_read,
.write = pmic_write,
};
static struct driver_d pmic_driver = {
.name = "mc13783",
.probe = pmic_probe,
};
static int pmic_init(void)
{
register_driver(&pmic_driver);
return 0;
}
device_initcall(pmic_init);
|