static void twd_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) { unsigned long ctrl; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: /* timer load already set up */ ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_PERIODIC; __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD); gic_enable_ppi(clk->irq); break; case CLOCK_EVT_MODE_ONESHOT: /* period set, and timer enabled in 'next_event' hook */ ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; gic_enable_ppi(clk->irq); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: ctrl = 0; gic_disable_ppi(clk->irq); } __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); }
int __cpuinit local_timer_setup(struct clock_event_device *evt) { struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER]; /* Use existing clock_event for cpu 0 */ if (!smp_processor_id()) return; writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); if (!local_clock_event) { writel(0, clock->regbase + TIMER_ENABLE); writel(0, clock->regbase + TIMER_CLEAR); writel(~0, clock->regbase + TIMER_MATCH_VAL); } evt->irq = clock->irq.irq; evt->name = "local_timer"; evt->features = CLOCK_EVT_FEAT_ONESHOT; evt->rating = clock->clockevent.rating; evt->set_mode = msm_timer_set_mode; evt->set_next_event = msm_timer_set_next_event; evt->shift = clock->clockevent.shift; evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift); evt->max_delta_ns = clockevent_delta2ns(0xf0000000 >> clock->shift, evt); evt->min_delta_ns = clockevent_delta2ns(4, evt); local_clock_event = evt; gic_enable_ppi(clock->irq.irq); clockevents_register_device(evt); return 0; }
/* * Setup the local clock events for a CPU. */ void __cpuinit twd_timer_setup(struct clock_event_device *clk) { if (!twd_clk) twd_clk = twd_get_clock(); if (!IS_ERR_OR_NULL(twd_clk)) twd_timer_rate = clk_get_rate(twd_clk); else twd_calibrate_rate(); clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; clk->rating = 350; clk->set_mode = twd_set_mode; clk->set_next_event = twd_set_next_event; __get_cpu_var(twd_ce) = clk; clockevents_config_and_register(clk, twd_timer_rate, 0xf, 0xffffffff); /* Make sure our local interrupt controller has this enabled */ gic_enable_ppi(clk->irq); }
int __cpuinit twd_clockchip_init(virtual_addr_t base, virtual_addr_t ref_counter_addr, u32 ref_counter_freq, u32 ppi_hirq) { int rc; u32 cpu = vmm_smp_processor_id(); struct twd_clockchip *cc = &this_cpu(twd_cc); memset(cc, 0, sizeof(struct twd_clockchip)); twd_caliberate_freq(base, ref_counter_addr, ref_counter_freq); vmm_sprintf(cc->name, "twd/%d", cpu); cc->base = base; cc->clkchip.name = cc->name; cc->clkchip.hirq = ppi_hirq; cc->clkchip.rating = 350; cc->clkchip.cpumask = vmm_cpumask_of(cpu); cc->clkchip.features = VMM_CLOCKCHIP_FEAT_PERIODIC | VMM_CLOCKCHIP_FEAT_ONESHOT; cc->clkchip.shift = 20; cc->clkchip.mult = vmm_clockchip_hz2mult(twd_freq_hz, cc->clkchip.shift); cc->clkchip.min_delta_ns = vmm_clockchip_delta2ns(0xF, &cc->clkchip); cc->clkchip.max_delta_ns = vmm_clockchip_delta2ns(0xFFFFFFFF, &cc->clkchip); cc->clkchip.set_mode = &twd_clockchip_set_mode; cc->clkchip.set_next_event = &twd_clockchip_set_next_event; cc->clkchip.expire = &twd_clockchip_expire; cc->clkchip.priv = cc; if (!cpu) { /* Register interrupt handler */ if ((rc = vmm_host_irq_register(ppi_hirq, "twd", &twd_clockchip_irq_handler, cc))) { return rc; } /* Mark interrupt as per-cpu */ if ((rc = vmm_host_irq_mark_per_cpu(ppi_hirq))) { return rc; } } /* Explicitly enable local timer PPI in GIC * Note: Local timer requires PPI support hence requires GIC */ gic_enable_ppi(ppi_hirq); return vmm_clockchip_register(&cc->clkchip); }
/* * Setup the local clock events for a CPU. */ void __cpuinit twd_timer_setup(struct clock_event_device *clk) { twd_calibrate_rate(); clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; clk->rating = 350; clk->set_mode = twd_set_mode; clk->set_next_event = twd_set_next_event; clk->shift = 20; clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift); clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); clk->min_delta_ns = clockevent_delta2ns(0xf, clk); /* Make sure our local interrupt controller has this enabled */ gic_enable_ppi(clk->irq); clockevents_register_device(clk); }
int __cpuinit arch_smp_ipi_init(void) { int rc; if (vmm_smp_is_bootcpu()) { /* Register IPI1 interrupt handler */ rc = vmm_host_irq_register(1, "IPI1", &smp_ipi_handler, NULL); if (rc) { return rc; } /* Mark IPI1 interrupt as per-cpu */ rc = vmm_host_irq_mark_per_cpu(1); if (rc) { return rc; } } /* Explicitly enable IPI1 interrupt */ gic_enable_ppi(1); return VMM_OK; }
static int __cpuinit twd_clockchip_init(struct vmm_devtree_node *node) { int rc; u32 ref_cnt_freq; virtual_addr_t ref_cnt_addr; u32 cpu = vmm_smp_processor_id(); struct twd_clockchip *cc = &this_cpu(twd_cc); if (!twd_base) { rc = vmm_devtree_regmap(node, &twd_base, 0); if (rc) { goto fail; } } if (!twd_ppi_irq) { rc = vmm_devtree_irq_get(node, &twd_ppi_irq, 0); if (rc) { goto fail_regunmap; } } if (!twd_freq_hz) { /* First try to find TWD clock */ if (!twd_clk) { twd_clk = of_clk_get(node, 0); } if (!twd_clk) { twd_clk = clk_get_sys("smp_twd", NULL); } if (twd_clk) { /* Use TWD clock to find frequency */ rc = clk_prepare_enable(twd_clk); if (rc) { clk_put(twd_clk); goto fail_regunmap; } twd_freq_hz = clk_get_rate(twd_clk); } else { /* No TWD clock found hence caliberate */ rc = vmm_devtree_regmap(node, &ref_cnt_addr, 1); if (rc) { vmm_devtree_regunmap(node, ref_cnt_addr, 1); goto fail_regunmap; } if (vmm_devtree_read_u32(node, "ref-counter-freq", &ref_cnt_freq)) { vmm_devtree_regunmap(node, ref_cnt_addr, 1); goto fail_regunmap; } twd_caliberate_freq(twd_base, ref_cnt_addr, ref_cnt_freq); vmm_devtree_regunmap(node, ref_cnt_addr, 1); } } memset(cc, 0, sizeof(struct twd_clockchip)); vmm_sprintf(cc->name, "twd/%d", cpu); cc->clkchip.name = cc->name; cc->clkchip.hirq = twd_ppi_irq; cc->clkchip.rating = 350; cc->clkchip.cpumask = vmm_cpumask_of(cpu); cc->clkchip.features = VMM_CLOCKCHIP_FEAT_PERIODIC | VMM_CLOCKCHIP_FEAT_ONESHOT; vmm_clocks_calc_mult_shift(&cc->clkchip.mult, &cc->clkchip.shift, VMM_NSEC_PER_SEC, twd_freq_hz, 10); cc->clkchip.min_delta_ns = vmm_clockchip_delta2ns(0xF, &cc->clkchip); cc->clkchip.max_delta_ns = vmm_clockchip_delta2ns(0xFFFFFFFF, &cc->clkchip); cc->clkchip.set_mode = &twd_clockchip_set_mode; cc->clkchip.set_next_event = &twd_clockchip_set_next_event; cc->clkchip.priv = cc; if (vmm_smp_is_bootcpu()) { /* Register interrupt handler */ if ((rc = vmm_host_irq_register(twd_ppi_irq, "twd", &twd_clockchip_irq_handler, cc))) { goto fail_regunmap; } /* Mark interrupt as per-cpu */ if ((rc = vmm_host_irq_mark_per_cpu(twd_ppi_irq))) { goto fail_unreg_irq; } } /* Explicitly enable local timer PPI in GIC * Note: Local timer requires PPI support hence requires GIC */ gic_enable_ppi(twd_ppi_irq); rc = vmm_clockchip_register(&cc->clkchip); if (rc) { goto fail_unreg_irq; } return VMM_OK; fail_unreg_irq: if (vmm_smp_is_bootcpu()) { vmm_host_irq_unregister(twd_ppi_irq, cc); } fail_regunmap: vmm_devtree_regunmap(node, twd_base, 0); fail: return rc; }
int __cpuinit generic_timer_clockchip_init(void) { int rc; u32 irq[3], num_irqs, val; struct vmm_clockchip *cc; struct vmm_devtree_node *node; /* Find generic timer device tree node */ node = vmm_devtree_find_matching(NULL, generic_timer_match); if (!node) { return VMM_ENODEV; } /* Determine generic timer frequency */ if (generic_timer_hz == 0) { rc = vmm_devtree_clock_frequency(node, &generic_timer_hz); if (rc) { /* Use preconfigured counter frequency * in absence of dts node */ generic_timer_hz = generic_timer_reg_read(GENERIC_TIMER_REG_FREQ); } else if (generic_timer_freq_writeable()) { /* Program the counter frequency as per the dts node */ generic_timer_reg_write(GENERIC_TIMER_REG_FREQ, generic_timer_hz); } } if (generic_timer_hz == 0) { return VMM_EFAIL; } /* Get hypervisor timer irq number */ rc = vmm_devtree_irq_get(node, &irq[GENERIC_HYPERVISOR_TIMER], GENERIC_HYPERVISOR_TIMER); if (rc) { return rc; } /* Get physical timer irq number */ rc = vmm_devtree_irq_get(node, &irq[GENERIC_PHYSICAL_TIMER], GENERIC_PHYSICAL_TIMER); if (rc) { return rc; } /* Get virtual timer irq number */ rc = vmm_devtree_irq_get(node, &irq[GENERIC_VIRTUAL_TIMER], GENERIC_VIRTUAL_TIMER); if (rc) { return rc; } /* Number of generic timer irqs */ num_irqs = vmm_devtree_irq_count(node); if (!num_irqs) { return VMM_EFAIL; } /* Ensure hypervisor timer is stopped */ generic_timer_stop(); /* Create generic hypervisor timer clockchip */ cc = vmm_zalloc(sizeof(struct vmm_clockchip)); if (!cc) { return VMM_EFAIL; } cc->name = "gen-hyp-timer"; cc->hirq = irq[GENERIC_HYPERVISOR_TIMER]; cc->rating = 400; cc->cpumask = vmm_cpumask_of(vmm_smp_processor_id()); cc->features = VMM_CLOCKCHIP_FEAT_ONESHOT; vmm_clocks_calc_mult_shift(&cc->mult, &cc->shift, VMM_NSEC_PER_SEC, generic_timer_hz, 10); cc->min_delta_ns = vmm_clockchip_delta2ns(0xF, cc); cc->max_delta_ns = vmm_clockchip_delta2ns(0x7FFFFFFF, cc); cc->set_mode = &generic_timer_set_mode; cc->set_next_event = &generic_timer_set_next_event; cc->priv = NULL; /* Register hypervisor timer clockchip */ rc = vmm_clockchip_register(cc); if (rc) { goto fail_free_cc; } if (!vmm_smp_processor_id()) { /* Register irq handler for hypervisor timer */ rc = vmm_host_irq_register(irq[GENERIC_HYPERVISOR_TIMER], "gen-hyp-timer", &generic_hyp_timer_handler, cc); if (rc) { goto fail_unreg_cc; } /* Mark hypervisor timer irq as per-CPU */ if ((rc = vmm_host_irq_mark_per_cpu(cc->hirq))) { goto fail_unreg_htimer; } if (num_irqs > 1) { /* Register irq handler for physical timer */ rc = vmm_host_irq_register(irq[GENERIC_PHYSICAL_TIMER], "gen-phys-timer", &generic_phys_timer_handler, NULL); if (rc) { goto fail_unreg_htimer; } /* Mark physical timer irq as per-CPU */ rc = vmm_host_irq_mark_per_cpu( irq[GENERIC_PHYSICAL_TIMER]); if (rc) { goto fail_unreg_ptimer; } } if (num_irqs > 2) { /* Register irq handler for virtual timer */ rc = vmm_host_irq_register(irq[GENERIC_VIRTUAL_TIMER], "gen-virt-timer", &generic_virt_timer_handler, NULL); if (rc) { goto fail_unreg_ptimer; } /* Mark virtual timer irq as per-CPU */ rc = vmm_host_irq_mark_per_cpu( irq[GENERIC_VIRTUAL_TIMER]); if (rc) { goto fail_unreg_vtimer; } } } if (num_irqs > 1) { val = generic_timer_reg_read(GENERIC_TIMER_REG_HCTL); val |= GENERIC_TIMER_HCTL_KERN_PCNT_EN; val |= GENERIC_TIMER_HCTL_KERN_PTMR_EN; generic_timer_reg_write(GENERIC_TIMER_REG_HCTL, val); } for (val = 0; val < num_irqs; val++) { gic_enable_ppi(irq[val]); } return VMM_OK; fail_unreg_vtimer: if (!vmm_smp_processor_id() && num_irqs > 2) { vmm_host_irq_unregister(irq[GENERIC_HYPERVISOR_TIMER], &generic_virt_timer_handler); } fail_unreg_ptimer: if (!vmm_smp_processor_id() && num_irqs > 1) { vmm_host_irq_unregister(irq[GENERIC_PHYSICAL_TIMER], &generic_phys_timer_handler); } fail_unreg_htimer: if (!vmm_smp_processor_id()) { vmm_host_irq_unregister(irq[GENERIC_HYPERVISOR_TIMER], &generic_hyp_timer_handler); } fail_unreg_cc: vmm_clockchip_register(cc); fail_free_cc: vmm_free(cc); return rc; }
int __cpuinit twd_clockchip_init(virtual_addr_t ref_counter_addr, u32 ref_counter_freq) { int rc; u32 cpu = vmm_smp_processor_id(); struct vmm_devtree_node *node; struct twd_clockchip *cc = &this_cpu(twd_cc); node = vmm_devtree_find_matching(NULL, twd_match); if (!node) { return VMM_ENODEV; } if (!twd_base) { rc = vmm_devtree_regmap(node, &twd_base, 0); if (rc) { return rc; } } if (!twd_ppi_irq) { rc = vmm_devtree_irq_get(node, &twd_ppi_irq, 0); if (rc) { return rc; } } twd_caliberate_freq(twd_base, ref_counter_addr, ref_counter_freq); memset(cc, 0, sizeof(struct twd_clockchip)); vmm_sprintf(cc->name, "twd/%d", cpu); cc->clkchip.name = cc->name; cc->clkchip.hirq = twd_ppi_irq; cc->clkchip.rating = 350; cc->clkchip.cpumask = vmm_cpumask_of(cpu); cc->clkchip.features = VMM_CLOCKCHIP_FEAT_PERIODIC | VMM_CLOCKCHIP_FEAT_ONESHOT; vmm_clocks_calc_mult_shift(&cc->clkchip.mult, &cc->clkchip.shift, VMM_NSEC_PER_SEC, twd_freq_hz, 10); cc->clkchip.min_delta_ns = vmm_clockchip_delta2ns(0xF, &cc->clkchip); cc->clkchip.max_delta_ns = vmm_clockchip_delta2ns(0xFFFFFFFF, &cc->clkchip); cc->clkchip.set_mode = &twd_clockchip_set_mode; cc->clkchip.set_next_event = &twd_clockchip_set_next_event; cc->clkchip.priv = cc; if (!cpu) { /* Register interrupt handler */ if ((rc = vmm_host_irq_register(twd_ppi_irq, "twd", &twd_clockchip_irq_handler, cc))) { return rc; } /* Mark interrupt as per-cpu */ if ((rc = vmm_host_irq_mark_per_cpu(twd_ppi_irq))) { return rc; } } /* Explicitly enable local timer PPI in GIC * Note: Local timer requires PPI support hence requires GIC */ gic_enable_ppi(twd_ppi_irq); return vmm_clockchip_register(&cc->clkchip); }