summaryrefslogtreecommitdiffstats
path: root/utils/hwstub/stub/stmp/target.c
blob: 47768aab0d60df0c3a525a09d31f2f2dd4ff8211 (plain)
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2013 by Amaury Pouly
 *
 * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "stddef.h"
#include "target.h"
#include "system.h"
#include "logf.h"

#define __REG_SET(reg)  (*((volatile uint32_t *)(&reg + 1)))
#define __REG_CLR(reg)  (*((volatile uint32_t *)(&reg + 2)))
#define __REG_TOG(reg)  (*((volatile uint32_t *)(&reg + 3)))

#define __BLOCK_SFTRST  (1 << 31)
#define __BLOCK_CLKGATE (1 << 30)

#define __XTRACT(reg, field)    ((reg & reg##__##field##_BM) >> reg##__##field##_BP)
#define __XTRACT_EX(val, field)    (((val) & field##_BM) >> field##_BP)
#define __FIELD_SET(reg, field, val) reg = (reg & ~reg##__##field##_BM) | (val << reg##__##field##_BP)

/**
 *
 * Global
 *
 */

enum stmp_family_t
{
    UNKNOWN,
    STMP3600,
    STMP3700,
    STMP3770,
    STMP3780
};

static enum stmp_family_t g_stmp_family = UNKNOWN;
static int g_atexit = HWSTUB_ATEXIT_OFF;

/**
 *
 * Power
 *
 */

#define HW_POWER_BASE       0x80044000

void power_off(void)
{
    switch(g_stmp_family)
    {
        case STMP3600:
            *(volatile uint32_t *)(HW_POWER_BASE + 0xc0) = 0x3e770014;
            break;
        case STMP3700:
        case STMP3770:
            *(volatile uint32_t *)(HW_POWER_BASE + 0xe0) = 0x3e770001;
            break;
        case STMP3780:
            *(volatile uint32_t *)(HW_POWER_BASE + 0x100) = 0x3e770001;
            break;
        default:
            break;
    }
}

/**
 *
 * Clkctrl
 *
 */

#define HW_CLKCTRL_BASE     0x80040000

#define HW_CLKCTRL_PLLCTRL0 (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x0))
#define HW_CLKCTRL_PLLCTRL0__BYPASS         (1 << 17) /* STMP3600 only */
#define HW_CLKCTRL_PLLCTRL0__POWER          (1 << 16)
#define HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS    (1 << 18)

#define HW_CLKCTRL_PLLCTRL1 (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x10))
#define HW_CLKCTRL_PLLCTRL1__LOCK           (1 << 31)

/* STMP3600 only */
#define HW_CLKCTRL_CPUCLKCTRL   (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x20))
#define HW_CLKCTRL_CPUCLKCTRL__DIV_BP   0
#define HW_CLKCTRL_CPUCLKCTRL__DIV_BM   0x3ff
#define HW_CLKCTRL_CPUCLKCTRL__WAIT_PLL_LOCK    (1 << 30)

/* STMP3600 */
#define HW_CLKCTRL_HBUSCLKCTRL  (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x30))

/* STMP3600 only */
#define HW_CLKCTRL_XBUSCLKCTRL  (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x40))
#define HW_CLKCTRL_XBUSCLKCTRL__DIV_BP   0
#define HW_CLKCTRL_XBUSCLKCTRL__DIV_BM   0x3ff

/* STMP3600 only */
#define HW_CLKCTRL_UTMICLKCTRL  (*(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x70))
#define HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK30M_GATE    (1 << 30)
#define HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK120M_GATE   (1 << 31)

void clkctrl_reset(void)
{
    switch(g_stmp_family)
    {
        case STMP3600:
            *(volatile uint32_t *)(HW_POWER_BASE + 0xc0) = 0x3e770002;
            break;
        case STMP3700:
        case STMP3770:
            *(volatile uint32_t *)(HW_CLKCTRL_BASE + 0xf0) = 0x1;
            break;
        case STMP3780:
            *(volatile uint32_t *)(HW_CLKCTRL_BASE + 0x120) = 0x1;
            break;
        default:
            break;
    }
}

/**
 *
 * Digctl
 *
 */

/* Digital control */
#define HW_DIGCTL_BASE          0x8001C000
#define HW_DIGCTL_CTRL          (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0))
#define HW_DIGCTL_CTRL__USB_CLKGATE (1 << 2)

/* STMP3700+ */
#define HW_DIGCTL_MICROSECONDS  (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0xC0))
/* STMP3600 */
#define HW_DIGCTL_MICROSECONDS2 (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0xB0))

#define HW_DIGCTL_CHIPID        (*(volatile uint32_t *)(HW_DIGCTL_BASE + 0x310))
#define HW_DIGCTL_CHIPID__PRODUCT_CODE_BP   16
#define HW_DIGCTL_CHIPID__PRODUCT_CODE_BM   0xffff0000
#define HW_DIGCTL_CHIPID__REVISION_BP       0
#define HW_DIGCTL_CHIPID__REVISION_BM       0xff

#define HZ  1000000

/**
 *
 * USB PHY
 *
 */
/* USB Phy */
#define HW_USBPHY_BASE          0x8007C000
#define HW_USBPHY_PWD           (*(volatile uint32_t *)(HW_USBPHY_BASE + 0))

#define HW_USBPHY_CTRL          (*(volatile uint32_t *)(HW_USBPHY_BASE + 0x30))

void target_init(void)
{
    /* detect family */
    uint16_t product_code = __XTRACT(HW_DIGCTL_CHIPID, PRODUCT_CODE);
    if(product_code >= 0x3600 && product_code < 0x3700)
    {
        logf("identified STMP3600 family\n");
        g_stmp_family = STMP3600;
    }
    else if(product_code == 0x3700)
    {
        logf("identified STMP3700 family\n");
        g_stmp_family = STMP3700;
    }
    else if(product_code == 0x37b0)
    {
        logf("identified STMP3770 family\n");
        g_stmp_family = STMP3770;
    }
    else if(product_code == 0x3780)
    {
        logf("identified STMP3780 family\n");
        g_stmp_family = STMP3780;
    }
    else
        logf("cannot identify family: 0x%x\n", product_code);

    if(g_stmp_family == STMP3600)
    {
        /* CPU clock is always derived from PLL, if we switch to PLL, cpu will
         * run at 480 MHz unprepared ! That's bad so prepare to run at slow sleed
         * (1.2MHz) for a safe transition */
        HW_CLKCTRL_CPUCLKCTRL = HW_CLKCTRL_CPUCLKCTRL__WAIT_PLL_LOCK | 400;
        /* We need to ensure that XBUS < HBUS but HBUS will be 1.2 MHz after the
         * switch so lower XBUS too */
        HW_CLKCTRL_XBUSCLKCTRL = 20;
        /* Power PLL */
        __REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
        HW_CLKCTRL_PLLCTRL0 = (HW_CLKCTRL_PLLCTRL0 & ~0x3ff) | 480;
        /* Wait lock */
        while(!(HW_CLKCTRL_PLLCTRL1 & HW_CLKCTRL_PLLCTRL1__LOCK));
        /* Switch to PLL source */
        __REG_CLR(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__BYPASS;
        /* Get back XBUS = 24 MHz and CPU = HBUS = 64MHz */
        HW_CLKCTRL_CPUCLKCTRL = 7;
        HW_CLKCTRL_HBUSCLKCTRL = 2;
        HW_CLKCTRL_XBUSCLKCTRL = 1;
        __REG_CLR(HW_CLKCTRL_UTMICLKCTRL) = HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK120M_GATE;
        __REG_CLR(HW_CLKCTRL_UTMICLKCTRL) = HW_CLKCTRL_UTMICLKCTRL__UTMI_CLK30M_GATE;
    }
    else
        __REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__POWER;
    /* enable USB PHY PLL */
    __REG_SET(HW_CLKCTRL_PLLCTRL0) = HW_CLKCTRL_PLLCTRL0__EN_USB_CLKS;
    /* power up USB PHY */
    __REG_CLR(HW_USBPHY_CTRL) = __BLOCK_CLKGATE | __BLOCK_SFTRST;
    HW_USBPHY_PWD = 0;
    /* enable USB controller */
    __REG_CLR(HW_DIGCTL_CTRL) = HW_DIGCTL_CTRL__USB_CLKGATE;
}

static struct usb_resp_info_stmp_t g_stmp;
static struct usb_resp_info_target_t g_target =
{
    .id = HWSTUB_TARGET_STMP,
    .name = "STMP3600 / STMP3700 / STMP3780 (i.MX233)"
};

int target_get_info(int info, void **buffer)
{
    if(info == HWSTUB_INFO_STMP)
    {
        g_stmp.chipid = __XTRACT(HW_DIGCTL_CHIPID, PRODUCT_CODE);
        g_stmp.rev = __XTRACT(HW_DIGCTL_CHIPID, REVISION);
        g_stmp.is_supported = g_stmp_family != 0;
        *buffer = &g_stmp;
        return sizeof(g_stmp);
    }
    else if(info == HWSTUB_INFO_TARGET)
    {
        *buffer = &g_target;
        return sizeof(g_target);
    }
    else
        return -1;
}

int target_atexit(int method)
{
    g_atexit = method;
    return 0;
}

void target_exit(void)
{
    switch(g_atexit)
    {
        case HWSTUB_ATEXIT_OFF:
            power_off();
            // fallthrough in case of return
        case HWSTUB_ATEXIT_REBOOT:
            clkctrl_reset();
            // fallthrough in case of return
        case HWSTUB_ATEXIT_NOP:
        default:
            return;
    }
}

void target_udelay(int us)
{
    volatile uint32_t *reg = g_stmp_family == STMP3600 ? &HW_DIGCTL_MICROSECONDS2 : &HW_DIGCTL_MICROSECONDS;
    uint32_t cur = *reg;
    uint32_t end = cur + us;
    if(cur < end)
        while(*reg < end) {}
    else
        while(*reg >= cur) {}
}

void target_mdelay(int ms)
{
    return target_udelay(ms * 1000);
}