summaryrefslogtreecommitdiffstats
path: root/firmware/target/coldfire/iriver/h300/pcf50606-h300.c
blob: 120021bd3cb85d1b11c1c11036d284b0a044badc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2005 by Linus Nielsen Feltzing
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "system.h"
#include "kernel.h"
#include "pcf50606.h"
#include "button-target.h"
#include "logf.h"

/* Used by the bootloader to find out what caused the device to start */
unsigned char pcf50606_intregs[3];

static bool usb_ch_enabled = false;

/* These voltages were determined by measuring the output of the PCF50606
   on a running H300, and verified by disassembling the original firmware */
static void set_voltages(void)
{
    static const unsigned char buf[5] =
    {
        0xf4,   /* IOREGC  = 2.9V, ON in all states  */
        0xef,   /* D1REGC  = 2.4V, ON in all states  */
        0x18,   /* D2REGC  = 3.3V, OFF in all states */
        0xf0,   /* D3REGC  = 2.5V, ON in all states  */
        0xef,   /* LPREGC1 = 2.4V, ON in all states  */
    };

    pcf50606_write_multiple(0x23, buf, 5);
}

static void init_pmu_interrupts(void)
{
    /* inital data is interrupt masks */
    unsigned char data[3] =
    {
        ~0x00,
        ~0x00,
        ~0x06, /* unmask ACDREM, ACDINS  */
    };

    /* make sure GPI6 interrupt is off before unmasking anything */
    and_l(~0x0f000000, &INTPRI5);  /* INT38 - Priority 0 (Off) */

    /* unmask the PMU interrupts we want to service */
    pcf50606_write_multiple(0x05, data, 3);
    /* clear INT1-3 as these are left set after standby */
    pcf50606_read_multiple(0x02, pcf50606_intregs, 3);

    /* Set to read pcf50606 INT but keep GPI6 off until init completes */
    and_l(~0x00000040, &GPIO_ENABLE);
    or_l(0x00000040, &GPIO_FUNCTION);
    or_l(0x00004000, &GPIO_INT_EN);     /* GPI6 H-L */
}

static inline void enable_pmu_interrupts(void)
{
    /* clear pending GPI6 interrupts first or it may miss the first
       H-L transition */
    or_l(0x00004000, &GPIO_INT_CLEAR);
    or_l(0x03000000, &INTPRI5); /* INT38 - Priority 3 */
}

/* enables/disables USB charging
 * ATTENTION: make sure to set the irq level
 *   to highest before calling this function! */
void pcf50606_set_usb_charging(bool on)
{
    /* USB charging is controlled by GPOOD0:
       High-Z: Charge enable
       Pulled down: Charge disable
    */
    if (on)
        pcf50606_write(0x39, 0x00);
    else
        pcf50606_write(0x39, 0x07);

    usb_ch_enabled = on;

    logf("pcf50606_set_usb_charging(%s)\n", on ? "on" : "off" );
}

bool pcf50606_usb_charging_enabled(void)
{
    /* TODO: read the state of the GPOOD2 register... */
    return usb_ch_enabled;
}

void pcf50606_init(void)
{
    pcf50606_i2c_init();

    /* initialize pmu interrupts but don't service them yet */
    init_pmu_interrupts();
    set_voltages();

    pcf50606_write(0x08, 0x60); /* Wake on USB and charger insertion */
    pcf50606_write(0x09, 0x05); /* USB and ON key debounce: 14ms */
    pcf50606_write(0x29, 0x1C); /* Disable the unused MBC module */

    pcf50606_set_usb_charging(false); /* Disable USB charging atm. */

    pcf50606_write(0x35, 0x13); /* Backlight PWM = 512Hz 50/50 */
    pcf50606_write(0x3a, 0x3b); /* PWM output on GPOOD1 */

    pcf50606_write(0x33, 0x8c); /* Accessory detect: ACDAPE=1, THRSHLD=2.20V */

    enable_pmu_interrupts();    /* allow GPI6 interrupts from PMU now */
}

/* PMU interrupt */
void GPI6(void) __attribute__ ((interrupt_handler));
void GPI6(void)
{
    unsigned char data[3]; /* 0 = INT1, 1 = INT2, 2 = INT3 */

    /* Clear pending GPI6 interrupts */
    or_l(0x00004000, &GPIO_INT_CLEAR);

    /* clear pending interrupts from pcf50606 */
    pcf50606_read_multiple(0x02, data, 3);

    if (data[2] & 0x06)
    {
        /* ACDINS/ACDREM */
        /* Check if the button driver should actually scan main buttons or not
           - bias towards "yes" out of paranoia. */
        button_enable_scan((data[2] & 0x02) != 0 ||
                           (pcf50606_read(0x33) & 0x01) != 0);
    }
}