cregit-Linux how code gets into the kernel

Release 4.11 arch/arm/mach-mvebu/pmsu.c

/*
 * Power Management Service Unit(PMSU) support for Armada 370/XP platforms.
 *
 * Copyright (C) 2012 Marvell
 *
 * Yehuda Yitschak <yehuday@marvell.com>
 * Gregory Clement <gregory.clement@free-electrons.com>
 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 *
 * The Armada 370 and Armada XP SOCs have a power management service
 * unit which is responsible for powering down and waking up CPUs and
 * other SOC units
 */


#define pr_fmt(fmt) "mvebu-pmsu: " fmt

#include <linux/clk.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mbus.h>
#include <linux/mvebu-pmsu.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/tlbflush.h>
#include "common.h"
#include "pmsu.h"


#define PMSU_BASE_OFFSET    0x100

#define PMSU_REG_SIZE	    0x1000

/* PMSU MP registers */

#define PMSU_CONTROL_AND_CONFIG(cpu)	    ((cpu * 0x100) + 0x104)

#define PMSU_CONTROL_AND_CONFIG_DFS_REQ		BIT(18)

#define PMSU_CONTROL_AND_CONFIG_PWDDN_REQ	BIT(16)

#define PMSU_CONTROL_AND_CONFIG_L2_PWDDN	BIT(20)


#define PMSU_CPU_POWER_DOWN_CONTROL(cpu)    ((cpu * 0x100) + 0x108)


#define PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP	BIT(0)


#define PMSU_STATUS_AND_MASK(cpu)	    ((cpu * 0x100) + 0x10c)

#define PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT	BIT(16)

#define PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT	BIT(17)

#define PMSU_STATUS_AND_MASK_IRQ_WAKEUP		BIT(20)

#define PMSU_STATUS_AND_MASK_FIQ_WAKEUP		BIT(21)

#define PMSU_STATUS_AND_MASK_DBG_WAKEUP		BIT(22)

#define PMSU_STATUS_AND_MASK_IRQ_MASK		BIT(24)

#define PMSU_STATUS_AND_MASK_FIQ_MASK		BIT(25)


#define PMSU_EVENT_STATUS_AND_MASK(cpu)     ((cpu * 0x100) + 0x120)

#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE        BIT(1)

#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK   BIT(17)


#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124)

/* PMSU fabric registers */

#define L2C_NFABRIC_PM_CTL		    0x4

#define L2C_NFABRIC_PM_CTL_PWR_DOWN		BIT(20)

/* PMSU delay registers */

#define PMSU_POWERDOWN_DELAY		    0xF04

#define PMSU_POWERDOWN_DELAY_PMU		BIT(1)

#define PMSU_POWERDOWN_DELAY_MASK		0xFFFE

#define PMSU_DFLT_ARMADA38X_DELAY	        0x64

/* CA9 MPcore SoC Control registers */


#define MPCORE_RESET_CTL		    0x64

#define MPCORE_RESET_CTL_L2			BIT(0)

#define MPCORE_RESET_CTL_DEBUG			BIT(16)


#define SRAM_PHYS_BASE  0xFFFF0000

#define BOOTROM_BASE    0xFFF00000

#define BOOTROM_SIZE    0x100000


#define ARMADA_370_CRYPT0_ENG_TARGET   0x9

#define ARMADA_370_CRYPT0_ENG_ATTR     0x1

extern void ll_disable_coherency(void);
extern void ll_enable_coherency(void);

extern void armada_370_xp_cpu_resume(void);
extern void armada_38x_cpu_resume(void);


static phys_addr_t pmsu_mp_phys_base;

static void __iomem *pmsu_mp_base;


static void *mvebu_cpu_resume;


static const struct of_device_id of_pmsu_table[] = {
	{ .compatible = "marvell,armada-370-pmsu", },
	{ .compatible = "marvell,armada-370-xp-pmsu", },
	{ .compatible = "marvell,armada-380-pmsu", },
	{ /* end of list */ },
};


void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) { writel(__pa_symbol(boot_addr), pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT2696.30%150.00%
Florian Fainelli13.70%150.00%
Total27100.00%2100.00%

extern unsigned char mvebu_boot_wa_start; extern unsigned char mvebu_boot_wa_end; /* * This function sets up the boot address workaround needed for SMP * boot on Armada 375 Z1 and cpuidle on Armada 370. It unmaps the * BootROM Mbus window, and instead remaps a crypto SRAM into which a * custom piece of code is copied to replace the problematic BootROM. */
int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target, unsigned int crypto_eng_attribute, phys_addr_t resume_addr_reg) { void __iomem *sram_virt_base; u32 code_len = &mvebu_boot_wa_end - &mvebu_boot_wa_start; mvebu_mbus_del_window(BOOTROM_BASE, BOOTROM_SIZE); mvebu_mbus_add_window_by_id(crypto_eng_target, crypto_eng_attribute, SRAM_PHYS_BASE, SZ_64K); sram_virt_base = ioremap(SRAM_PHYS_BASE, SZ_64K); if (!sram_virt_base) { pr_err("Unable to map SRAM to setup the boot address WA\n"); return -ENOMEM; } memcpy(sram_virt_base, &mvebu_boot_wa_start, code_len); /* * The last word of the code copied in SRAM must contain the * physical base address of the PMSU register. We * intentionally store this address in the native endianness * of the system. */ __raw_writel((unsigned long)resume_addr_reg, sram_virt_base + code_len - 4); iounmap(sram_virt_base); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT107100.00%1100.00%
Total107100.00%1100.00%


static int __init mvebu_v7_pmsu_init(void) { struct device_node *np; struct resource res; int ret = 0; np = of_find_matching_node(NULL, of_pmsu_table); if (!np) return 0; pr_info("Initializing Power Management Service Unit\n"); if (of_address_to_resource(np, 0, &res)) { pr_err("unable to get resource\n"); ret = -ENOENT; goto out; } if (of_device_is_compatible(np, "marvell,armada-370-xp-pmsu")) { pr_warn(FW_WARN "deprecated pmsu binding\n"); res.start = res.start - PMSU_BASE_OFFSET; res.end = res.start + PMSU_REG_SIZE - 1; } if (!request_mem_region(res.start, resource_size(&res), np->full_name)) { pr_err("unable to request region\n"); ret = -EBUSY; goto out; } pmsu_mp_phys_base = res.start; pmsu_mp_base = ioremap(res.start, resource_size(&res)); if (!pmsu_mp_base) { pr_err("unable to map registers\n"); release_mem_region(res.start, resource_size(&res)); ret = -ENOMEM; goto out; } out: of_node_put(np); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Petazzoni12057.14%114.29%
Gregory CLEMENT8540.48%457.14%
JiSheng Zhang52.38%228.57%
Total210100.00%7100.00%


static void mvebu_v7_pmsu_enable_l2_powerdown_onidle(void) { u32 reg; if (pmsu_mp_base == NULL) return; /* Enable L2 & Fabric powerdown in Deep-Idle mode - Fabric */ reg = readl(pmsu_mp_base + L2C_NFABRIC_PM_CTL); reg |= L2C_NFABRIC_PM_CTL_PWR_DOWN; writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT41100.00%2100.00%
Total41100.00%2100.00%

enum pmsu_idle_prepare_flags { PMSU_PREPARE_NORMAL = 0, PMSU_PREPARE_DEEP_IDLE = BIT(0), PMSU_PREPARE_SNOOP_DISABLE = BIT(1), }; /* No locking is needed because we only access per-CPU registers */
static int mvebu_v7_pmsu_idle_prepare(unsigned long flags) { unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); u32 reg; if (pmsu_mp_base == NULL) return -EINVAL; /* * Adjust the PMSU configuration to wait for WFI signal, enable * IRQ and FIQ as wakeup events, set wait for snoop queue empty * indication and mask IRQ and FIQ from CPU */ reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP | PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT | PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK; writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); /* ask HW to power down the L2 Cache if needed */ if (flags & PMSU_PREPARE_DEEP_IDLE) reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN; /* request power down */ reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ; writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); if (flags & PMSU_PREPARE_SNOOP_DISABLE) { /* Disable snoop disable by HW - SW is taking care of it */ reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP; writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT14595.39%480.00%
Thomas Petazzoni74.61%120.00%
Total152100.00%5100.00%


int armada_370_xp_pmsu_idle_enter(unsigned long deepidle) { unsigned long flags = PMSU_PREPARE_SNOOP_DISABLE; int ret; if (deepidle) flags |= PMSU_PREPARE_DEEP_IDLE; ret = mvebu_v7_pmsu_idle_prepare(flags); if (ret) return ret; v7_exit_coherency_flush(all); ll_disable_coherency(); dsb(); wfi(); /* If we are here, wfi failed. As processors run out of * coherency for some time, tlbs might be stale, so flush them */ local_flush_tlb_all(); ll_enable_coherency(); /* Test the CR_C bit and set it if it was cleared */ asm volatile( "mrc p15, 0, r0, c1, c0, 0 \n\t" "tst r0, %0 \n\t" "orreq r0, r0, #(1 << 2) \n\t" "mcreq p15, 0, r0, c1, c0, 0 \n\t" "isb " : : "Ir" (CR_C) : "r0"); pr_debug("Failed to suspend the system\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT73100.00%6100.00%
Total73100.00%6100.00%


static int armada_370_xp_cpu_suspend(unsigned long deepidle) { return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter); }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT1794.44%150.00%
Thomas Petazzoni15.56%150.00%
Total18100.00%2100.00%


int armada_38x_do_cpu_suspend(unsigned long deepidle) { unsigned long flags = 0; if (deepidle) flags |= PMSU_PREPARE_DEEP_IDLE; mvebu_v7_pmsu_idle_prepare(flags); /* * Already flushed cache, but do it again as the outer cache * functions dirty the cache with spinlocks */ v7_exit_coherency_flush(louis); scu_power_mode(mvebu_get_scu_base(), SCU_PM_POWEROFF); cpu_do_idle(); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT48100.00%1100.00%
Total48100.00%1100.00%


static int armada_38x_cpu_suspend(unsigned long deepidle) { return cpu_suspend(false, armada_38x_do_cpu_suspend); }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT18100.00%1100.00%
Total18100.00%1100.00%

/* No locking is needed because we only access per-CPU registers */
void mvebu_v7_pmsu_idle_exit(void) { unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); u32 reg; if (pmsu_mp_base == NULL) return; /* cancel ask HW to power down the L2 Cache if possible */ reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); /* cancel Enable wakeup events and mask interrupts */ reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); reg &= ~(PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP); reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; reg &= ~PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT; reg &= ~(PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK); writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT110100.00%2100.00%
Total110100.00%2100.00%


static int mvebu_v7_cpu_pm_notify(struct notifier_block *self, unsigned long action, void *hcpu) { if (action == CPU_PM_ENTER) { unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cpu_resume); } else if (action == CPU_PM_EXIT) { mvebu_v7_pmsu_idle_exit(); } return NOTIFY_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT59100.00%3100.00%
Total59100.00%3100.00%

static struct notifier_block mvebu_v7_cpu_pm_notifier = { .notifier_call = mvebu_v7_cpu_pm_notify, }; static struct platform_device mvebu_v7_cpuidle_device;
static int broken_idle(struct device_node *np) { if (of_property_read_bool(np, "broken-idle")) { pr_warn("CPU idle is currently broken: disabling\n"); return 1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Donnefort33100.00%1100.00%
Total33100.00%1100.00%


static __init int armada_370_cpuidle_init(void) { struct device_node *np; phys_addr_t redirect_reg; np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); if (!np) return -ENODEV; if (broken_idle(np)) goto end; /* * On Armada 370, there is "a slow exit process from the deep * idle state due to heavy L1/L2 cache cleanup operations * performed by the BootROM software". To avoid this, we * replace the restart code of the bootrom by a a simple jump * to the boot address. Then the code located at this boot * address will take care of the initialization. */ redirect_reg = pmsu_mp_phys_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(0); mvebu_setup_boot_addr_wa(ARMADA_370_CRYPT0_ENG_TARGET, ARMADA_370_CRYPT0_ENG_ATTR, redirect_reg); mvebu_cpu_resume = armada_370_xp_cpu_resume; mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-370"; end: of_node_put(np); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT7984.04%250.00%
Vincent Donnefort1414.89%125.00%
Sachin Kamat11.06%125.00%
Total94100.00%4100.00%


static __init int armada_38x_cpuidle_init(void) { struct device_node *np; void __iomem *mpsoc_base; u32 reg; pr_warn("CPU idle is currently broken on Armada 38x: disabling\n"); return 0; np = of_find_compatible_node(NULL, NULL, "marvell,armada-380-coherency-fabric"); if (!np) return -ENODEV; if (broken_idle(np)) goto end; of_node_put(np); np = of_find_compatible_node(NULL, NULL, "marvell,armada-380-mpcore-soc-ctrl"); if (!np) return -ENODEV; mpsoc_base = of_iomap(np, 0); BUG_ON(!mpsoc_base); /* Set up reset mask when powering down the cpus */ reg = readl(mpsoc_base + MPCORE_RESET_CTL); reg |= MPCORE_RESET_CTL_L2; reg |= MPCORE_RESET_CTL_DEBUG; writel(reg, mpsoc_base + MPCORE_RESET_CTL); iounmap(mpsoc_base); /* Set up delay */ reg = readl(pmsu_mp_base + PMSU_POWERDOWN_DELAY); reg &= ~PMSU_POWERDOWN_DELAY_MASK; reg |= PMSU_DFLT_ARMADA38X_DELAY; reg |= PMSU_POWERDOWN_DELAY_PMU; writel(reg, pmsu_mp_base + PMSU_POWERDOWN_DELAY); mvebu_cpu_resume = armada_38x_cpu_resume; mvebu_v7_cpuidle_device.dev.platform_data = armada_38x_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-38x"; end: of_node_put(np); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT17490.62%360.00%
Vincent Donnefort178.85%120.00%
Thomas Petazzoni10.52%120.00%
Total192100.00%5100.00%


static __init int armada_xp_cpuidle_init(void) { struct device_node *np; np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); if (!np) return -ENODEV; if (broken_idle(np)) goto end; mvebu_cpu_resume = armada_370_xp_cpu_resume; mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-xp"; end: of_node_put(np); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT5880.56%480.00%
Vincent Donnefort1419.44%120.00%
Total72100.00%5100.00%


static int __init mvebu_v7_cpu_pm_init(void) { struct device_node *np; int ret; np = of_find_matching_node(NULL, of_pmsu_table); if (!np) return 0; of_node_put(np); /* * Currently the CPU idle support for Armada 38x is broken, as * the CPU hotplug uses some of the CPU idle functions it is * broken too, so let's disable it */ if (of_machine_is_compatible("marvell,armada380")) { cpu_hotplug_disable(); pr_warn("CPU hotplug support is currently broken on Armada 38x: disabling\n"); } if (of_machine_is_compatible("marvell,armadaxp")) ret = armada_xp_cpuidle_init(); else if (of_machine_is_compatible("marvell,armada370")) ret = armada_370_cpuidle_init(); else if (of_machine_is_compatible("marvell,armada380")) ret = armada_38x_cpuidle_init(); else return 0; if (ret) return ret; mvebu_v7_pmsu_enable_l2_powerdown_onidle(); if (mvebu_v7_cpuidle_device.name) platform_device_register(&mvebu_v7_cpuidle_device); cpu_pm_register_notifier(&mvebu_v7_cpu_pm_notifier); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT12999.23%685.71%
Thomas Petazzoni10.77%114.29%
Total130100.00%7100.00%

arch_initcall(mvebu_v7_cpu_pm_init); early_initcall(mvebu_v7_pmsu_init);
static void mvebu_pmsu_dfs_request_local(void *data) { u32 reg; u32 cpu = smp_processor_id(); unsigned long flags; local_irq_save(flags); /* Prepare to enter idle */ reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK; writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); /* Request the DFS transition */ reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); reg |= PMSU_CONTROL_AND_CONFIG_DFS_REQ; writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); /* The fact of entering idle will trigger the DFS transition */ wfi(); /* * We're back from idle, the DFS transition has completed, * clear the idle wait indication. */ reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); local_irq_restore(flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Petazzoni129100.00%1100.00%
Total129100.00%1100.00%


int mvebu_pmsu_dfs_request(int cpu) { unsigned long timeout; int hwcpu = cpu_logical_map(cpu); u32 reg; /* Clear any previous DFS DONE event */ reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE; writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); /* Mask the DFS done interrupt, since we are going to poll */ reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); reg |= PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); /* Trigger the DFS on the appropriate CPU */ smp_call_function_single(cpu, mvebu_pmsu_dfs_request_local, NULL, false); /* Poll until the DFS done event is generated */ timeout = jiffies + HZ; while (time_before(jiffies, timeout)) { reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); if (reg & PMSU_EVENT_STATUS_AND_MASK_DFS_DONE) break; udelay(10); } if (time_after(jiffies, timeout)) return -ETIME; /* Restore the DFS mask to its original state */ reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Petazzoni182100.00%1100.00%
Total182100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Gregory CLEMENT147171.30%1854.55%
Thomas Petazzoni49724.09%721.21%
Vincent Donnefort783.78%13.03%
JiSheng Zhang70.34%26.06%
Ben Dooks60.29%13.03%
Jason Cooper10.05%13.03%
Uwe Kleine-König10.05%13.03%
Sachin Kamat10.05%13.03%
Florian Fainelli10.05%13.03%
Total2063100.00%33100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.