/***************************************************************************/
/*                             Druckersoftware                             */
/*                    Copyright (C) Carl Valentin GmbH                     */
/***************************************************************************/

/*
 * Timer.c
 *
 *  Created on: Apr 7, 2016
 *      Author: preitinger
 */

/*. IMPORT ================================================================*/
#include "Timer.h"
#include <linux/of.h>
#include <linux/io-mapping.h>
/*. ENDIMPORT =============================================================*/

/*. LOCAL =================================================================*/

/*-------------------------------- Makros ---------------------------------*/

/*--------------------------- Typdeklarationen ----------------------------*/

/*------------------------------ Prototypen -------------------------------*/

/*------------------------ Konstantendeklarationen ------------------------*/

/*------------------------- modulglobale Variable -------------------------*/

/*. ENDLOCAL ==============================================================*/

int
Timer_create(Timer* p)
{
    p->us = 0;
    p->dn = NULL;
    p->t = NULL;
    p->started = false;

    p->dn = of_find_node_by_path("/ocp/timer@48044000");

    if (!p->dn)
    {
        pr_alert("timer@48044000 not available!\n");
        return -1;
    }
    p->dn = of_node_get(p->dn);
    if (!p->dn)
    {
        return -1;
    }
    // TODO begin port
    dump_stack(); // REMOVE ME
//    p->t = omap_dm_timer_request_by_node(p->dn);
    // TODO end port

    if (!p->t)
    {
        pr_alert("omap_dm_timer_request_by_node failed!\n");
        of_node_put(p->dn);
        return -1;
    }

    pr_info("timer created: p->t=%p\n", p->t);
    return 0;
}

int
Timer_lowLoadStart(Timer* p, unsigned int load)
{
    if (p && p->t)
    {
//        int res;
        struct io_mapping* timerMap = NULL;
        void __iomem* a;

        if (p->started) {
            pr_alert("%s: Timer already started\n", __func__);
            return -1;
        }

        timerMap = io_mapping_create_wc(0x48044000, 0x00001000);
        if (!timerMap)
        {
            return -1;
        }
        pr_info("timerMap=%p\n", timerMap);
        a = io_mapping_map_wc(timerMap, 0, 0x00001000);
        pr_info("mapped a=%p\n", a);

        if (!a)
        {
            io_mapping_free(timerMap);
            timerMap = NULL;
            return -1;
        }

        pr_info("vor revision\n");
        // TODO begin port
        dump_stack(); // REMOVE ME
//        pr_info("timer->revision=%d, load=%d\n", p->t->revision, load);
        // TODO end port

        // TODO begin port
        dump_stack(); // REMOVE ME
//        // neu
//
//        // unbedingt erst mal enablen damit power management d.h. clocks aktiv
//        omap_dm_timer_enable(p->t); // erhoeht usecount, wird durch anschliessendes omap_dm_timer_disable(p->t) wieder ausbalanciert
//        pr_info("timer enabled\n");
//
//        res = omap_dm_timer_set_int_disable(p->t,
//                OMAP_TIMER_INT_CAPTURE | OMAP_TIMER_INT_MATCH);
//
//        if (0 == res)
//        {
//            res = omap_dm_timer_set_int_enable(p->t, OMAP_TIMER_INT_OVERFLOW);
//            pr_info("res of omap_dm_timer_set_int_enable=%d\n", res);
//
//            if (0 == res) {
//                // read_status nicht unbedingt in diesem zustand erlaubt, also weglassen:
////                pr_info("vor timer_read_status\n");
////                pr_info("omap_dm_timer_read_status(p->t)=%x\n",
////                        omap_dm_timer_read_status(p->t));
//                pr_info("schreibe irqstatus fuer %p\n", p->t);
//                res = omap_dm_timer_write_status(p->t, OMAP_TIMER_INT_OVERFLOW);
//                pr_info("res of timer_write_status: %d\n", res);
//#if 1
//                if (0 == res) {
//                    //        iowrite32(OMAP_TIMER_INT_OVERFLOW, a + 0x28);
//                    pr_info("\n==============> ACHTUNG ACHTUNG ;-) schreibe irq_eoi, d.h. adresse=%p\n\n", a + 0x20);
//                    iowrite32(0, a + 0x20);
//                }
//#else
//      pr_info("\n==========> ACHTUNG schreibe nicht irq_eoi!\n");
//#endif
//            }
//        }
//
//        io_mapping_unmap(a);
//        a = NULL;
//        io_mapping_free(timerMap);
//        timerMap = NULL;
//
//        if (0 == res)
//        {
//            pr_info("load=%x\n", load);
//
//            res = omap_dm_timer_set_load_start(p->t, 1, load);
//            pr_info("res of set_load_start: %d\n", res);
//            if (res == 0) {
//                p->started = true;
//            }
//        }
//
//        omap_dm_timer_disable(p->t); // falls start geklappt hat, wird nur usecount wieder ausbalanciert, ansonsten wird richtig disabled
//
//        return res;
//        // ende neu
        // TODO end port

        // alt
//        omap_dm_timer_write_status(t2, OMAP_TIMER_INT_OVERFLOW); // sicherheitshalber immer erst interrupt clearen vor dem start
//
//        res = omap_dm_timer_set_int_enable(p->t, OMAP_TIMER_INT_OVERFLOW);
//        pr_info("res of omap_dm_timer_set_int_enable=%d\n", res);
//        res = omap_dm_timer_set_load_start(p->t, 1, load); // TODO unsicher ob omap_dm_timer_set_load oder omap_dm_timer_set_load_start zu verwenden
//        if (res) {
//            pr_alert("omap_dm_timer_set_load_start failed for load=%u\n", load);
//        } else {
//            pr_info("timer geladen und gestartet\n");
//        }
        // ende alt

//        res = omap_dm_timer_trigger(p->t);
//        pr_info("res of omap_dm_timer_trigger: %d\n", res);
    }
    else
    {
        pr_alert("illegal state for lowLoadStart\n");
        return -1;
    }

    return -1;
}

int
Timer_stop(Timer* this)
{
    int res;

    if (this && this->t) {
        if (!this->started) {
            pr_err("%s: was not started\n", __func__);
            return -1;
        }

        // TODO begin port
        dump_stack(); // REMOVE ME
//        res = omap_dm_timer_stop(this->t);
        res = -1;
        // TODO end port

        if (res)
        {
            pr_err("res of omap_dm_timer_stop: %d\n", res);
            return -1;
        }
        else
        {
            pr_info("timer stopped\n");
            this->started = false;
            return 0;
        }
    } else {
        return -1;
    }
}

void
Timer_delete(Timer* p)
{
    // TODO begin port
    dump_stack(); // REMOVE ME
//    if (!p) return;
//
//    if (p->t)
//    {
//        int res;
//        if (p->started) {
//            res = omap_dm_timer_stop(p->t);
//
//            if (res) {
//                pr_alert("omap_dm_timer_stop returned %d\n", res);
//            }
//
//            p->started = false;
//        }
//
//        res = omap_dm_timer_free(p->t);
//
//        if (res)
//        {
//            pr_alert("omap_dm_timer_free returned %d\n", res);
//        }
//
//        p->t = NULL;
//        pr_info("timer freed\n");
//    }
//    if (p->dn)
//    {
//        of_node_put(p->dn);
//        p->dn = NULL;
//    }
//    pr_info("timer node put\n");
    // TODO end port
}

#define verboseSleep(s) { \
    pr_info("Schlafe %d secs\n", s); \
    ssleep(s); \
}

#define dumpTimerReg(name, off) { \
    pr_info("%s: %x\n", (name), timerReg(off)); \
}

// TODO nur voruebergehend zum testen
#define timerReg(off) (ioread32(a + (off)))

// TODO begin port
//void
//testRequest(void)
//{
//    struct omap_dm_timer* t = omap_dm_timer_request_specific(4);
//    pr_info("t=%p\n", t);
//
//    if (t)
//    {
//        int res = omap_dm_timer_free(t);
//        pr_info("omap_dm_timer_free returned %d\n", res);
//    }
//
//    {
//        struct device_node * dn;
//        dn = of_find_node_by_path("/ocp/timer@48044000");
//        pr_info("dn=%p\n", dn);
//
//        if (dn)
//        {
//            struct device_node* dn2 = of_node_get(dn);
//            pr_info("of_node_get: dn2=%p\n", dn2);
//
//            if (dn2)
//            {
//                struct omap_dm_timer* t2 = omap_dm_timer_request_by_node(dn2);
//                pr_info("t2=%p\n", t2);
//
//                if (t2)
//                {
//                    int res;
//                    struct io_mapping* timerMap = NULL;
//                    void __iomem* a;
//                    unsigned int secs1 = 1;
//                    unsigned int secs2 = 2;
//
//                    res = omap_dm_timer_set_load_start(t2, 0, 0xffff0600); // 0xffff05fe entspricht (0xffffffff-0xffff0600+1)*31.25us = 2000000us
//                    pr_info("res of set_load_start: %d\n", res);
//                    res = omap_dm_timer_set_int_enable(t2,
//                            OMAP_TIMER_INT_OVERFLOW);
//                    pr_info("res of omap_dm_timer_set_int_enable=%d\n", res);
//
//                    pr_info("mappe timer speicher...\n");
//                    timerMap = io_mapping_create_wc(0x48044000, 0x00001000);
//
//                    a = io_mapping_map_wc(timerMap, 0);
//                    pr_info("mapped a=%p\n", a);
//
//                    verboseSleep(secs1);
//                    pr_info("irqstatus_raw=%x\n", timerReg(0x24));
//
//                    verboseSleep(secs2);
//                    pr_info("Nach 3s: irqstatus_raw=%x\n", timerReg(0x24));
//                    pr_info("irqstatus=%x\n", timerReg(0x28));
//
//                    io_mapping_unmap(a);
//                    io_mapping_free(timerMap);
//                    res = omap_dm_timer_free(t2);
//                    pr_info("omap_dm_timer_free returned %d\n", res);
//                }
//            }
//
//            of_node_put(dn2);
//        }
//    }
//
//}
//
//void
//testTimerLoop(void)
//{
////    struct omap_dm_timer* t = omap_dm_timer_request_specific(4);
////    pr_info("t=%p\n", t);
////
////    if (t) {
////        int res = omap_dm_timer_free(t);
////        pr_info("omap_dm_timer_free returned %d\n", res);
////    }
//
//    {
//        struct device_node * dn;
//        dn = of_find_node_by_path("/ocp/timer@48044000");
//        pr_info("dn=%p\n", dn);
//
//        if (dn)
//        {
//            struct device_node* dn2 = of_node_get(dn);
//            pr_info("of_node_get: dn2=%p\n", dn2);
//
//            if (dn2)
//            {
//                struct omap_dm_timer* t2 = omap_dm_timer_request_by_node(dn2);
//                pr_info("t2=%p\n", t2);
//
//                if (t2)
//                {
//                    int res;
//                    struct io_mapping* timerMap = NULL;
//                    void __iomem* a;
//                    int reloadSecs = 5;
//                    uint32_t rldVal;
//                    int i;
//
//                    pr_info("mappe timer speicher...\n");
//                    timerMap = io_mapping_create_wc(0x48044000, 0x00001000);
//                    a = io_mapping_map_wc(timerMap, 0);
//                    pr_info("mapped a=%p\n", a);
//
//                    pr_info("zu Beginn: irqstatus_raw=%x\n", timerReg(0x24));
//                    pr_info("zu Beginn: omap_dm_timer_read_status(t2)=%x\n",
//                            omap_dm_timer_read_status(t2));
//                    pr_info("zu Beginn: irqenable_set=%x\n", timerReg(0x2c));
//                    verboseSleep(1);
//                    pr_info("zu Beginn: irqstatus=%x\n", timerReg(0x28));
//                    verboseSleep(1);
//                    pr_info("nach lesen irqstatus: irqstatus_raw=%x\n",
//                            timerReg(0x24));
//
//                    res = omap_dm_timer_set_int_enable(t2,
//                            OMAP_TIMER_INT_OVERFLOW);
//                    pr_info("res of omap_dm_timer_set_int_enable=%d\n", res);
//                    pr_info("nach set_int_enable: irqenable_set=%x\n",
//                            timerReg(0x2c));
//
//                    rldVal = 0xffffffff - reloadSecs * 100000000 / 3125;
//                    pr_info("omap_dm_timer_read_status(t2)=%x\n",
//                            omap_dm_timer_read_status(t2));
//                    pr_info("schreibe irqstatus\n");
//                    iowrite32(OMAP_TIMER_INT_OVERFLOW, a + 0x28);
//
//                    pr_info("rldVal=%x\n", rldVal);
//                    res = omap_dm_timer_set_load_start(t2, 1, rldVal);
//                    pr_info("res of set_load_start: %d\n", res);
//
//                    for (i = 0; i < 15; ++i)
//                    {
//                        verboseSleep(1);
//                        pr_info("irqstatus_raw=%x\n", timerReg(0x24));
//                        pr_info("irqstatus=%x\n", timerReg(0x28));
//                        pr_info("omap_dm_timer_read_status(t2)=%x\n",
//                                omap_dm_timer_read_status(t2));
//                        pr_info("timer4 tcrr=%x\n", timerReg(0x3c));
//
//                        // simuliere langsame "interruptbehandlung"
//                        if (timerReg(0x28) & OMAP_TIMER_INT_OVERFLOW)
//                        {
//                            pr_info("-----> timer overflow!\n\n");
////                            iowrite32(OMAP_TIMER_INT_OVERFLOW, a + 0x28); // zuruecksetzen
//                            omap_dm_timer_write_status(t2,
//                                    OMAP_TIMER_INT_OVERFLOW);
//                        }
//
//                    }
//
//                    io_mapping_unmap(a);
//                    io_mapping_free(timerMap);
//                    res = omap_dm_timer_free(t2);
//                    pr_info("omap_dm_timer_free returned %d\n", res);
//                }
//            }
//
//            of_node_put(dn2);
//        }
//    }
//
//}
// TODO end port
