cvmx-app-hotplug.c revision 215990
1/***********************license start***************
2 * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17
18 *   * Neither the name of Cavium Networks nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22
23 * This Software, including technical data, may be subject to U.S. export  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40/**
41 * @file
42 *
43 * Provides APIs for applications to register for hotplug. It also provides
44 * APIs for requesting shutdown of a running target application.
45 *
46 * <hr>$Revision: $<hr>
47 */
48
49#include "cvmx-app-hotplug.h"
50#include "cvmx-spinlock.h"
51
52//#define DEBUG 1
53
54#ifndef CVMX_BUILD_FOR_LINUX_USER
55
56static CVMX_SHARED cvmx_spinlock_t cvmx_app_hotplug_sync_lock = { CVMX_SPINLOCK_UNLOCKED_VAL };
57static CVMX_SHARED cvmx_spinlock_t cvmx_app_hotplug_lock = { CVMX_SPINLOCK_UNLOCKED_VAL };
58static CVMX_SHARED cvmx_app_hotplug_info_t *cvmx_app_hotplug_info_ptr = NULL;
59
60static void __cvmx_app_hotplug_shutdown(int irq_number, uint64_t registers[32], void *user_arg);
61static void __cvmx_app_hotplug_sync(void);
62static void __cvmx_app_hotplug_reset(void);
63
64/**
65 * This routine registers an application for hotplug. It installs a handler for
66 * any incoming shutdown request. It also registers a callback routine from the
67 * application. This callback is invoked when the application receives a
68 * shutdown notification.
69 *
70 * This routine only needs to be called once per application.
71 *
72 * @param fn      Callback routine from the application.
73 * @param arg     Argument to the application callback routine.
74 * @return        Return 0 on success, -1 on failure
75 *
76 */
77int cvmx_app_hotplug_register(void(*fn)(void*), void* arg)
78{
79    /* Find the list of applications launched by bootoct utility. */
80
81    if (!(cvmx_app_hotplug_info_ptr = cvmx_app_hotplug_get_info(cvmx_sysinfo_get()->core_mask)))
82    {
83        /* Application not launched by bootoct? */
84        printf("ERROR: cmvx_app_hotplug_register() failed\n");
85        return -1;
86    }
87
88    /* Register the callback */
89    cvmx_app_hotplug_info_ptr->data = CAST64(arg);
90    cvmx_app_hotplug_info_ptr->shutdown_callback = CAST64(fn);
91
92#ifdef DEBUG
93    cvmx_dprintf("cvmx_app_hotplug_register(): coremask 0x%x valid %d\n",
94                  cvmx_app_hotplug_info_ptr->coremask, cvmx_app_hotplug_info_ptr->valid);
95#endif
96
97    cvmx_interrupt_register(CVMX_IRQ_MBOX0, __cvmx_app_hotplug_shutdown, NULL);
98
99    return 0;
100}
101
102/**
103 * Activate the current application core for receiving hotplug shutdown requests.
104 *
105 * This routine makes sure that each core belonging to the application is enabled
106 * to receive the shutdown notification and also provides a barrier sync to make
107 * sure that all cores are ready.
108 */
109int cvmx_app_hotplug_activate(void)
110{
111    /* Make sure all application cores are activating */
112    __cvmx_app_hotplug_sync();
113
114    cvmx_spinlock_lock(&cvmx_app_hotplug_lock);
115
116    if (!cvmx_app_hotplug_info_ptr)
117    {
118        cvmx_spinlock_unlock(&cvmx_app_hotplug_lock);
119        printf("ERROR: This application is not registered for hotplug\n");
120	return -1;
121    }
122
123    /* Enable the interrupt before we mark the core as activated */
124    cvmx_interrupt_unmask_irq(CVMX_IRQ_MBOX0);
125
126    cvmx_app_hotplug_info_ptr->hotplug_activated_coremask |= (1<<cvmx_get_core_num());
127
128#ifdef DEBUG
129    cvmx_dprintf("cvmx_app_hotplug_activate(): coremask 0x%x valid %d sizeof %d\n",
130                 cvmx_app_hotplug_info_ptr->coremask, cvmx_app_hotplug_info_ptr->valid,
131                 sizeof(*cvmx_app_hotplug_info_ptr));
132#endif
133
134    cvmx_spinlock_unlock(&cvmx_app_hotplug_lock);
135
136    return 0;
137}
138
139/**
140 * This routine is only required if cvmx_app_hotplug_shutdown_request() was called
141 * with wait=0. This routine waits for the application shutdown to complete.
142 *
143 * @param coremask     Coremask the application is running on.
144 * @return             0 on success, -1 on error
145 *
146 */
147int cvmx_app_hotplug_shutdown_complete(uint32_t coremask)
148{
149    cvmx_app_hotplug_info_t *hotplug_info_ptr;
150
151    if (!(hotplug_info_ptr = cvmx_app_hotplug_get_info(coremask)))
152    {
153        printf("\nERROR: Failed to get hotplug info for coremask: 0x%x\n", (unsigned int)coremask);
154        return -1;
155    }
156
157    while(!hotplug_info_ptr->shutdown_done);
158
159    /* Clean up the hotplug info region for this app */
160    bzero(hotplug_info_ptr, sizeof(*hotplug_info_ptr));
161
162    return 0;
163}
164
165/**
166 * Disable recognition of any incoming shutdown request.
167 */
168
169void cvmx_app_hotplug_shutdown_disable(void)
170{
171    cvmx_interrupt_mask_irq(CVMX_IRQ_MBOX0);
172}
173
174/**
175 * Re-enable recognition of incoming shutdown requests.
176 */
177
178void cvmx_app_hotplug_shutdown_enable(void)
179{
180    cvmx_interrupt_unmask_irq(CVMX_IRQ_MBOX0);
181}
182
183/*
184 * ISR for the incoming shutdown request interrupt.
185 */
186static void __cvmx_app_hotplug_shutdown(int irq_number, uint64_t registers[32], void *user_arg)
187{
188    cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
189    uint32_t flags;
190
191    cvmx_interrupt_mask_irq(CVMX_IRQ_MBOX0);
192
193    /* Clear the interrupt */
194    cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()), 1);
195
196    /* Make sure the write above completes */
197    cvmx_read_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()));
198
199    if (!cvmx_app_hotplug_info_ptr)
200    {
201        printf("ERROR: Application is not registered for hotplug!\n");
202        return;
203    }
204
205    if (cvmx_app_hotplug_info_ptr->hotplug_activated_coremask != sys_info_ptr->core_mask)
206    {
207        printf("ERROR: Shutdown requested when not all app cores have activated hotplug\n"
208	       "Application coremask: 0x%x Hotplug coremask: 0x%x\n", (unsigned int)sys_info_ptr->core_mask,
209	       (unsigned int)cvmx_app_hotplug_info_ptr->hotplug_activated_coremask);
210	return;
211    }
212
213    /* Call the application's own callback function */
214    ((void(*)(void*))(long)cvmx_app_hotplug_info_ptr->shutdown_callback)(CASTPTR(void *, cvmx_app_hotplug_info_ptr->data));
215
216    __cvmx_app_hotplug_sync();
217
218    if (cvmx_coremask_first_core(sys_info_ptr->core_mask))
219    {
220        bzero(cvmx_app_hotplug_info_ptr, sizeof(*cvmx_app_hotplug_info_ptr));
221#ifdef DEBUG
222        cvmx_dprintf("__cvmx_app_hotplug_shutdown(): setting shutdown done! \n");
223#endif
224        cvmx_app_hotplug_info_ptr->shutdown_done = 1;
225    }
226
227    flags = cvmx_interrupt_disable_save();
228
229    __cvmx_app_hotplug_sync();
230
231    /* Reset the core */
232    __cvmx_app_hotplug_reset();
233}
234
235/*
236 * Reset the core. We just jump back to the reset vector for now.
237 */
238void __cvmx_app_hotplug_reset(void)
239{
240    /* Code from SecondaryCoreLoop from bootloader, sleep until we recieve
241       a NMI. */
242    __asm__ volatile (
243        ".set noreorder      \n"
244	"\tsync               \n"
245	"\tnop               \n"
246        "1:\twait            \n"
247        "\tb 1b              \n"
248	"\tnop               \n"
249	".set reorder        \n"
250	::
251    );
252}
253
254/*
255 * We need a separate sync operation from cvmx_coremask_barrier_sync() to
256 * avoid a deadlock on state.lock, since the application itself maybe doing a
257 * cvmx_coremask_barrier_sync().
258 */
259static void __cvmx_app_hotplug_sync(void)
260{
261    static CVMX_SHARED volatile uint32_t sync_coremask = 0;
262    cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
263
264    cvmx_spinlock_lock(&cvmx_app_hotplug_sync_lock);
265
266    sync_coremask |= cvmx_coremask_core(cvmx_get_core_num());
267
268    cvmx_spinlock_unlock(&cvmx_app_hotplug_sync_lock);
269
270    while (sync_coremask != sys_info_ptr->core_mask);
271}
272
273#endif /* CVMX_BUILD_FOR_LINUX_USER */
274
275/**
276 * Return the hotplug info structure (cvmx_app_hotplug_info_t) pointer for the
277 * application running on the given coremask.
278 *
279 * @param coremask     Coremask of application.
280 * @return             Returns hotplug info struct on success, NULL on failure
281 *
282 */
283cvmx_app_hotplug_info_t* cvmx_app_hotplug_get_info(uint32_t coremask)
284{
285    const struct cvmx_bootmem_named_block_desc *block_desc;
286    cvmx_app_hotplug_info_t *hip;
287    cvmx_app_hotplug_global_t *hgp;
288    int i;
289
290    block_desc = cvmx_bootmem_find_named_block(CVMX_APP_HOTPLUG_INFO_REGION_NAME);
291
292    if (!block_desc)
293    {
294        printf("ERROR: Hotplug info region is not setup\n");
295        return NULL;
296    }
297    else
298
299#ifdef CVMX_BUILD_FOR_LINUX_USER
300    {
301        size_t pg_sz = sysconf(_SC_PAGESIZE), size;
302        off_t offset;
303        char *vaddr;
304        int fd;
305
306        if ((fd = open("/dev/mem", O_RDWR)) == -1) {
307            perror("open");
308            return NULL;
309        }
310
311        /*
312         * We need to mmap() this memory, since this was allocated from the
313         * kernel bootup code and does not reside in the RESERVE32 region.
314         */
315        size = CVMX_APP_HOTPLUG_INFO_REGION_SIZE + pg_sz-1;
316        offset = block_desc->base_addr & ~(pg_sz-1);
317        if ((vaddr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED)
318        {
319            perror("mmap");
320            return NULL;
321        }
322
323        hgp = (cvmx_app_hotplug_global_t *)(vaddr + ( block_desc->base_addr & (pg_sz-1)));
324    }
325#else
326    hgp = cvmx_phys_to_ptr(block_desc->base_addr);
327#endif
328
329    hip = hgp->hotplug_info_array;
330
331#ifdef DEBUG
332    cvmx_dprintf("cvmx_app_hotplug_get_info(): hotplug_info phy addr 0x%llx ptr %p\n",
333                  block_desc->base_addr, hgp);
334#endif
335
336    /* Look for the current app's info */
337
338    for (i=0; i<CVMX_APP_HOTPLUG_MAX_APPS; i++)
339    {
340        if (hip[i].coremask == coremask)
341	{
342#ifdef DEBUG
343	    cvmx_dprintf("cvmx_app_hotplug_get_info(): coremask match %d -- coremask 0x%x valid %d\n",
344	                 i, hip[i].coremask, hip[i].valid);
345#endif
346
347	    return &hip[i];
348	}
349    }
350
351    return NULL;
352}
353
354/**
355 * This routine sends a shutdown request to a running target application.
356 *
357 * @param coremask     Coremask the application is running on.
358 * @param wait         1 - Wait for shutdown completion
359 *                     0 - Do not wait
360 * @return             0 on success, -1 on error
361 *
362 */
363
364int cvmx_app_hotplug_shutdown_request(uint32_t coremask, int wait)
365{
366    int i;
367    cvmx_app_hotplug_info_t *hotplug_info_ptr;
368
369    if (!(hotplug_info_ptr = cvmx_app_hotplug_get_info(coremask)))
370    {
371        printf("\nERROR: Failed to get hotplug info for coremask: 0x%x\n", (unsigned int)coremask);
372        return -1;
373    }
374
375    if (!hotplug_info_ptr->shutdown_callback)
376    {
377        printf("\nERROR: Target application has not registered for hotplug!\n");
378        return -1;
379    }
380
381    if (hotplug_info_ptr->hotplug_activated_coremask != coremask)
382    {
383        printf("\nERROR: Not all application cores have activated hotplug\n");
384        return -1;
385    }
386
387    /* Send IPIs to all application cores to request shutdown */
388    for (i=0; i<CVMX_MAX_CORES; i++) {
389    	if (coremask & (1<<i))
390		cvmx_write_csr(CVMX_CIU_MBOX_SETX(i), 1);
391    }
392
393    if (wait)
394    {
395        while (!hotplug_info_ptr->shutdown_done);
396
397        /* Clean up the hotplug info region for this application */
398        bzero(hotplug_info_ptr, sizeof(*hotplug_info_ptr));
399    }
400
401    return 0;
402}
403