/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2007 by Will Robertson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "cpu.h"
#include "kernel.h"
#include "thread.h"
#include "system.h"
#include "power.h"
#include "panic.h"
#include "ata.h"
#include "ata-target.h"
#include "clkctl-imx31.h"

static const struct ata_pio_timings
{
    uint16_t time_2w;       /* t2 during write */
    uint16_t time_2r;       /* t2 during read */
    uint8_t  time_1;        /* t1 */
    uint8_t  time_pio_rdx;  /* trd */
    uint8_t  time_4;        /* t4 */
    uint8_t  time_9;        /* t9 */
} pio_timings[5] =
{
    [0] =  /* PIO mode 0 */
    {
        .time_1 = 70,
        .time_2w = 290,
        .time_2r = 290,
        .time_4 = 30,
        .time_9 = 20
    },
    [1] = /* PIO mode 1 */
    {
        .time_1 = 50,
        .time_2w = 290,
        .time_2r = 290,
        .time_4 = 20,
        .time_9 = 15
    },
    [2] = /* PIO mode 2 */
    {
        .time_1 = 30,
        .time_2w = 290,
        .time_2r = 290,
        .time_4 = 15,
        .time_9 = 10
    },
    [3] = /* PIO mode 3 */
    {
        .time_1 = 30,
        .time_2w = 80,
        .time_2r = 80,
        .time_4 = 10,
        .time_9 = 10
    },
    [4] = /* PIO mode 4 */
    {
        .time_1 = 25,
        .time_2w = 70,
        .time_2r = 70,
        .time_4 = 10,
        .time_9 = 10
    },
};

static int pio_mode = 0; /* Setup mode 0 by default */

static void ata_wait_for_idle(void)
{
    while (!(ATA_INTERRUPT_PENDING & ATA_CONTROLLER_IDLE));
}

/* Setup the timing for PIO mode */
void ata_set_pio_timings(int mode)
{
    ata_wait_for_idle();

    const struct ata_pio_timings * const timings = &pio_timings[mode];
    /* T = period in nanoseconds */
    int T = 1000 * 1000 * 1000 / imx31_clkctl_get_ata_clk();

    pio_mode = mode;

    ATA_TIME_OFF = 3;
    ATA_TIME_ON = 3;

    ATA_TIME_1 = (timings->time_1 + T) / T;
    ATA_TIME_2W = (timings->time_2w + T) / T;
    ATA_TIME_2R = (timings->time_2r + T) / T;
    ATA_TIME_AX = (35 + T) / T; /* tA */
    ATA_TIME_PIO_RDX = 1;
    ATA_TIME_4 = (timings->time_4 + T) / T;
    ATA_TIME_9 = (timings->time_9 + T) / T;
}

void ata_reset(void)
{
    /* Be sure we're not busy */
    ata_wait_for_idle();

    ATA_INTF_CONTROL &= ~ATA_ATA_RST;
    sleep(1);
    ATA_INTF_CONTROL |= ATA_ATA_RST;
    sleep(1);

    ata_wait_for_idle();
}

void ata_enable(bool on)
{
    /* Unconditionally clock module before writing regs */
    imx31_clkctl_module_clock_gating(CG_ATA, CGM_ON_ALL);

    if (on)
    {
        ATA_INTF_CONTROL |= ATA_ATA_RST;
    }
    else
    {
        ata_wait_for_idle();

        ATA_INTF_CONTROL &= ~ATA_ATA_RST;

        /* Disable off - unclock ATA module */
        imx31_clkctl_module_clock_gating(CG_ATA, CGM_OFF);
    }
}

bool ata_is_coldstart(void)
{
    return true;
}

void ata_device_init(void)
{
    /* Make sure we're not in reset mode */
    ata_enable(true);

    /* mode may be switched later once identify info is ready in which
     * case the main driver calls back */
    ata_set_pio_timings(pio_mode);
}
