summaryrefslogtreecommitdiff
path: root/port/pusb2/freertos/usb_glue_phytium.c
blob: b61f400154aec43854240c0ad0ba721c8b9c2ca0 (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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/*
 * Copyright : (C) 2024 Phytium Information Technology, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Modify History:
 *  Ver   Who        Date         Changes
 * -----  ------     --------    --------------------------------------
 * 1.0   zhugengyu  2024/6/26 first commit
 */

/***************************** Include Files *********************************/
#include <stdio.h>
#include <string.h>

#include "fassert.h"
#include "fparameters.h"
#include "finterrupt.h"
#include "fcpu_info.h"
#include "fdebug.h"
#include "fcache.h"
#include "fmemory_pool.h"
#include "usb_config.h"

#ifdef CONFIG_USB_OTG_ENABLE
#include "FreeRTOS.h"
#include "task.h"
#include "usbotg_core.h"
#endif

extern void cps_otg_switch_to_host(uintptr_t reg_base);
extern void cps_otg_switch_to_device(uintptr_t reg_base);
extern uint8_t cps_otg_read_state(uintptr_t reg_base);

/************************** Constant Definitions *****************************/
#define USB_MEMP_TOTAL_SIZE SZ_1M

/**************************** Type Definitions *******************************/

/************************** Variable Definitions *****************************/
static FMemp memp;
static u8 memp_buf[USB_MEMP_TOTAL_SIZE] __attribute__((aligned(8))) = { 0 };

/* Memory pool reference counter - shared between DC and HC */
static u32 memp_ref_cnt = 0;

/* OTG mode flag: set when OTG is initialized */
static u8 g_otg_mode_active = 0;

/************************** Function Implementations *************************/

/**
 * @brief Initialize USB system memory pool
 *
 * Called by both DC and HC low_level_init. Memory pool is created on first call.
 */
void usb_sys_mem_init(void)
{
    if (FT_COMPONENT_IS_READY != memp.is_ready) {
        FASSERT(FT_SUCCESS == FMempInit(&memp, &memp_buf[0], &memp_buf[0] + USB_MEMP_TOTAL_SIZE));
    }
}

/**
 * @brief Deinitialize USB system memory pool
 *
 * Called by both DC and HC low_level_deinit. Memory pool is destroyed when
 * last user releases it.
 */
void usb_sys_mem_deinit(void)
{
    if (FT_COMPONENT_IS_READY == memp.is_ready) {
        FMempDeinit(&memp);
    }
}

/**
 * @brief Allocate aligned memory from USB memory pool
 */
void *usb_sys_malloc_align(size_t align, size_t size)
{
    void *result = FMempMallocAlign(&memp, size, align);

    if (result) {
        memset(result, 0U, size);
    }

    return result;
}

/**
 * @brief Allocate memory from USB memory pool (aligned to void*)
 */
void *usb_sys_mem_malloc(size_t size)
{
    return usb_sys_malloc_align(sizeof(void *), size);
}

/**
 * @brief Free memory back to USB memory pool
 */
void usb_sys_mem_free(void *ptr)
{
    if (NULL != ptr) {
        FMempFree(&memp, ptr);
    }
}

/**
 * @brief USB assert handler
 */
void usb_assert(const char *filename, int linenum)
{
    FAssert(filename, linenum, 0xff);
}

/**
 * @brief Increment memory pool reference counter
 *
 * Called by DC/HC low_level_init to track memory pool users.
 */
void usb_sys_mem_inc_ref(void)
{
    memp_ref_cnt++;
}

/**
 * @brief Decrement memory pool reference counter
 *
 * Called by DC/HC low_level_deinit. When counter reaches 0,
 * memory pool is deinitialized.
 * @return 1 if this was the last user (pool deinitialized), 0 otherwise
 */
u32 usb_sys_mem_dec_ref(void)
{
    if (memp_ref_cnt > 0) {
        memp_ref_cnt--;
    }
    return (memp_ref_cnt == 0) ? 1 : 0;
}

/**
 * @brief Get current memory pool reference count
 */
u32 usb_sys_mem_get_ref(void)
{
    return memp_ref_cnt;
}

/**
 * @brief Check if running inside interrupt context
 */
extern int vApplicationInIrq(void);
int xPortIsInsideInterrupt(void)
{
    return vApplicationInIrq();
}

/**
 * @brief Check if OTG mode is active
 */
u8 usb_otg_is_active(void)
{
    return g_otg_mode_active;
}

/**
 * @brief Set OTG mode active flag
 */
void usb_otg_set_active(u8 active)
{
    g_otg_mode_active = active;
}

#ifdef CONFIG_USB_OTG_ENABLE
/**
 * @brief Get USB OTG register base address
 */
unsigned long usb_otg_get_register_base(uint32_t id)
{
    FASSERT(id == CONFIG_USB_PUSB2_BUS_ID);
    return FUSB2_0_VHUB_BASE_ADDR;
}

/* External declaration for OTG IRQ handler provided by usbotg_core.c */
extern void USBOTG_IRQHandler(uint8_t busid);

/**
 * @brief OTG interrupt handler for PUSB2
 */
static void usb_otg_pusb2_interrupt_handler(s32 vector, void *param)
{
    (void)param;
    FASSERT(vector == FUSB2_0_VHUB_IRQ_NUM);
    USBOTG_IRQHandler((uint8_t)CONFIG_USB_PUSB2_BUS_ID);
}

/**
 * @brief Install OTG interrupt for PUSB2
 */
static void usb_otg_setup_interrupt(void)
{
    u32 cpu_id;
    u32 irq_num = FUSB2_0_VHUB_IRQ_NUM;
    u32 irq_priority = 13U;

    GetCpuId(&cpu_id);
    InterruptSetTargetCpus(irq_num, cpu_id);

    InterruptSetPriority(irq_num, irq_priority);

    /* Register OTG interrupt callback */
    InterruptInstall(irq_num,
                     usb_otg_pusb2_interrupt_handler,
                     NULL,
                     NULL);

    /* Enable IRQ */
    InterruptUmask(irq_num);

    FT_DEBUG_PRINT_I("OTG-GLUE", "Installed OTG interrupt, irq=%d\r\n", irq_num);
}

/**
 * @brief Initialize USB OTG hardware
 */
int usb_otg_init(uint8_t busid)
{
    FASSERT(busid == CONFIG_USB_PUSB2_BUS_ID);

    /* Install OTG interrupt handler */
    usb_otg_setup_interrupt();

    usb_otg_set_active(1);
    return 0;
}

/**
 * @brief Deinitialize USB OTG hardware
 */
int usb_otg_deinit(uint8_t busid)
{
    FASSERT(busid == CONFIG_USB_PUSB2_BUS_ID);
    usb_otg_set_active(0);
    return 0;
}

/**
 * @brief Switch USB OTG to Host mode
 */
void usb_otg_switch_to_host(uint8_t busid)
{
    uintptr_t reg_base = usb_otg_get_register_base(busid);
    cps_otg_switch_to_host(reg_base);
}

/**
 * @brief Switch USB OTG to Device mode
 */
void usb_otg_switch_to_device(uint8_t busid)
{
    uintptr_t reg_base = usb_otg_get_register_base(busid);
    cps_otg_switch_to_device(reg_base);
}

/* ==================== OTG Auto (software polling) ==================== */

#define OTG_AUTO_POLL_MS 500

enum {
    OTG_STATE_A_IDLE = 0x00,
    OTG_STATE_A_HOST = 0x03,
    OTG_STATE_B_IDLE = 0x10,
    OTG_STATE_B_PERIPHERAL = 0x11,
};

static int otg_auto_running = 0;
static uint8_t otg_auto_role = USBOTG_MODE_HOST;

static void otg_auto_poll_task(void *param)
{
    uint8_t busid = *(uint8_t *)param;
    uintptr_t reg_base = usb_otg_get_register_base(busid);
    int idle_ticks = 0;

    vTaskDelay(pdMS_TO_TICKS(300));

    while (otg_auto_running) {
        uint8_t state = cps_otg_read_state(reg_base);
        uint8_t role = 0;
        int need_switch = 0;
        int target_role = 0;

        switch (state) {
            case OTG_STATE_A_HOST:
                role = USBOTG_MODE_HOST;
                break;
            case OTG_STATE_B_PERIPHERAL:
                role = USBOTG_MODE_DEVICE;
                break;
            default:
                break;
        }

        if (role) {
            /* hardware detected a role: Host (U-disk) or Device (PC) */
            idle_ticks = 0;
            if (otg_auto_role != role) {
                need_switch = 1;
                target_role = role;
            }
        } else if (otg_auto_role == USBOTG_MODE_DEVICE) {
            /* role == 0 means nothing connected to either side.
             * If we're in Device mode, Host was killed; re-init so
             * VBUS comes back and U-disk can be powered/detected. */
            idle_ticks++;
            if (idle_ticks >= 1) {
                need_switch = 1;
                target_role = USBOTG_MODE_HOST;
                idle_ticks = 0;
            }
        }

        if (need_switch) {
            otg_auto_role = target_role;
            usbotg_trigger_role_change(busid, target_role);
            FT_DEBUG_PRINT_I("OTG-AUTO", "-> %s\r\n",
                             target_role == USBOTG_MODE_HOST ? "Host" : "Device");
        }

        vTaskDelay(pdMS_TO_TICKS(OTG_AUTO_POLL_MS));
    }
    vTaskDelete(NULL);
}

static TaskHandle_t g_otg_poll_task = NULL;

int usb_otg_auto_poll_start(uint8_t busid, int default_role)
{
    static uint8_t busid_arg;

    otg_auto_role = default_role;
    otg_auto_running = 1;
    busid_arg = busid;

    (void)xTaskCreate(otg_auto_poll_task,
                      "otgapol",
                      4096U,
                      &busid_arg,
                      configMAX_PRIORITIES - 2,
                      &g_otg_poll_task);
    return 0;
}

void usb_otg_auto_poll_stop(void)
{
    otg_auto_running = 0;
    if (g_otg_poll_task) {
        vTaskDelay(pdMS_TO_TICKS(OTG_AUTO_POLL_MS + 100));
        g_otg_poll_task = NULL;
    }
}

uint8_t usb_otg_auto_get_role(void)
{
    return otg_auto_role;
}

#endif /* CONFIG_USB_OTG_ENABLE */