File size: 4,616 Bytes
829b19c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Driver — OMAP3530 GPMC NOR Flash driver
 * Intel JS28F256M29EWH via GPMC CS0
 */

#include "nor_flash.h"

static hw_reg32_t nor_base(int cs) {
    return (hw_reg32_t)(NOR_FLASH_BASE + cs * 0x10000000UL);
}

static void nor_write_cmd(int cs, uint32_t offset, uint16_t cmd) {
    hw_reg32_t base = nor_base(cs);
    volatile uint16_t *flash = (volatile uint16_t *)base;
    flash[offset] = cmd;
    __asm__ volatile("dmb" ::: "memory");
}

static uint16_t nor_read_reg(int cs, uint32_t offset) {
    hw_reg32_t base = nor_base(cs);
    volatile uint16_t *flash = (volatile uint16_t *)base;
    return flash[offset];
}

int nor_flash_init(int cs) {
    hw_reg32_t gpmc = (hw_reg32_t)GPMC_BASE;

    /* Enable GPMC */
    reg_write32(gpmc + GPMC_SYSCONFIG, 0x10);  /* Smart idle */
    delay_cycles(100);

    /* Configure CS0 for 16-bit NOR flash, async */
    reg_write32(gpmc + GPMC_CONFIG1(cs),
        (0x00 << 0) |       /* DEVICETYPE: NOR async */
        (0x02 << 12) |      /* MWIDTH: 16-bit */
        (0x01 << 18)        /* WAITPIN: WAIT0 */
    );

    /* CS signal timings — conservative for initial bring-up */
    reg_write32(gpmc + GPMC_CONFIG2(cs),
        (0x08 << 0) |       /* CS_ON: 8 cycles */
        (0x1F << 8)         /* CS_WR_OFF: 31 cycles */
    );

    reg_write32(gpmc + GPMC_CONFIG3(cs),
        (0x02 << 0) |       /* ADV_ON: 2 cycles */
        (0x06 << 8)         /* ADV_WR_OFF: 6 cycles */
    );

    reg_write32(gpmc + GPMC_CONFIG4(cs),
        (0x03 << 0) |       /* OE_ON: 3 cycles */
        (0x0C << 8) |       /* OE_OFF: 12 cycles */
        (0x03 << 16) |      /* WE_ON: 3 cycles */
        (0x0C << 24)        /* WE_OFF: 12 cycles */
    );

    reg_write32(gpmc + GPMC_CONFIG5(cs),
        (0x02 << 0) |       /* RD_CYCLE: 2 extra */
        (0x02 << 8) |       /* WR_CYCLE: 2 extra */
        (0x02 << 16) |      /* RD_ACCESS: 2 cycles */
        (0x02 << 24)        /* WR_ACCESS: 2 cycles */
    );

    reg_write32(gpmc + GPMC_CONFIG6(cs),
        (0x00 << 0) |       /* WR_DATA_MUX: 0 */
        (0x02 << 8) |       /* WR_ACCESS: 2 cycles */
        (0x00 << 16)        /* CYCLE2CYCLE: no delay */
    );

    /* Set base address and enable CS */
    reg32_t config7 = (0x08 << 0) |    /* Base address bits [31:24] = 0x08 → 0x08000000 */
                      (0x0F << 8) |    /* Mask: 256MB region */
                      GPMC_CONFIG7_CSVALID;
    reg_write32(gpmc + GPMC_CONFIG7(cs), config7);

    /* Reset flash to read mode */
    nor_flash_reset(cs);

    return 0;
}

void nor_flash_read_id(int cs, uint16_t *manufacturer, uint16_t *device) {
    nor_write_cmd(cs, 0, NOR_CMD_READ_ID);
    delay_cycles(100);
    *manufacturer = nor_read_reg(cs, 0);
    *device = nor_read_reg(cs, 1);
    nor_flash_reset(cs);
}

void nor_flash_reset(int cs) {
    nor_write_cmd(cs, 0, NOR_CMD_RESET);
    delay_cycles(500);
}

int nor_flash_erase_sector(int cs, uint32_t offset) {
    nor_write_cmd(cs, 0, NOR_CMD_ERASE);
    nor_write_cmd(cs, offset >> 1, NOR_CMD_CONFIRM);

    /* Poll for completion (status bit 7 toggles when done) */
    volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
    uint32_t timeout = 5000000;
    while (timeout--) {
        uint16_t status = flash[offset >> 1];
        if (status & 0x0080)  /* DSQ7: done */
            break;
    }

    nor_flash_reset(cs);

    /* Verify erased (should read 0xFFFF) */
    if (flash[offset >> 1] != 0xFFFF)
        return -1;

    return 0;
}

int nor_flash_program_word(int cs, uint32_t offset, uint16_t data) {
    volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
    uint32_t word_offset = offset >> 1;

    nor_write_cmd(cs, 0, NOR_CMD_PROG);
    flash[word_offset] = data;
    __asm__ volatile("dmb" ::: "memory");

    /* Poll for completion */
    uint32_t timeout = 1000000;
    while (timeout--) {
        uint16_t status = flash[word_offset];
        if ((status & 0x0080) && (status == data))
            break;
    }

    nor_flash_reset(cs);

    if (flash[word_offset] != data)
        return -1;

    return 0;
}

int nor_flash_program_buf(int cs, uint32_t offset, const uint16_t *buf, uint32_t count) {
    for (uint32_t i = 0; i < count; i++) {
        if (nor_flash_program_word(cs, offset + i * 2, buf[i]) != 0)
            return -1;
    }
    return 0;
}

int nor_flash_read_buf(int cs, uint32_t offset, uint16_t *buf, uint32_t count) {
    volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
    uint32_t word_offset = offset >> 1;

    for (uint32_t i = 0; i < count; i++) {
        buf[i] = flash[word_offset + i];
    }
    return 0;
}