embox

Форк
0
/
bcm283x_gpio.c 
318 строк · 11.5 Кб
1
/**
2
 * @file
3
 * @brief
4
 *
5
 * @date    07.06.2021
6
 * @author  kpishere
7
 */
8

9
#include <assert.h>
10
#include <hal/reg.h>
11
#include <drivers/common/memory.h>
12
#include <embox/unit.h>
13
#include <lib/libds/array.h>
14
#include <kernel/printk.h>
15
#include <kernel/panic.h>
16
#include <kernel/irq.h>
17

18
#include <framework/mod/options.h>
19
#include "bcm283x_gpio.h"
20
#include <asm/delay.h>
21
#include <drivers/gpio/gpio_driver.h>
22

23
#define REG_BITS 32
24

25
#define PBASE                       OPTION_GET(NUMBER,base_addr)   
26
#define BCM283X_PINS_NUMBER         OPTION_GET(NUMBER,gpio_pins)
27
#define BCM283X_GPIO_PORTS_COUNT    OPTION_GET(NUMBER,gpio_ports)
28
#define GPU_IRQ                     OPTION_GET(NUMBER,gpu_irq_int)
29

30
#define BCM283X_PULL_PORTA_DEFLT_FLOAT    OPTION_GET(NUMBER,gpu_pullup_porta_float)
31
#define BCM283X_PULL_PORTA_DEFLT_DOWN     OPTION_GET(NUMBER,gpu_pullup_porta_down)
32
#define BCM283X_PULL_PORTA_DEFLT_UP       OPTION_GET(NUMBER,gpu_pullup_porta_up)
33

34
#define BCM283X_PULL_PORTB_DEFLT_FLOAT    OPTION_GET(NUMBER,gpu_pullup_portb_float)
35
#define BCM283X_PULL_PORTB_DEFLT_DOWN     OPTION_GET(NUMBER,gpu_pullup_portb_down)
36
#define BCM283X_PULL_PORTB_DEFLT_UP       OPTION_GET(NUMBER,gpu_pullup_portb_up)
37

38
#define PORT_PIN_TO_PINNUMBER(pt, pn)   ( ( ((pt) & 0x3 ) * REG_BITS ) + ((pn) % REG_BITS )) 
39
#define GPIO_ALTERNATE_INVERT(af) ( ((af) >> 17) & 0x07 )
40
#define BCM283X_GPIO_CHIP_ID    0
41

42
#define IRQ_NR_PORTA  49
43
#define IRQ_NR_PORTB  50
44
#define IRQ_NR_PORTC  51
45
#define IRQ_NR_ALL    52
46

47
typedef enum _GpioPullUpDn {
48
    GPUDNone = 0,
49
    GPUDDown = 1,
50
    GPUDUp  = 2
51
} GpioPullUpDn;
52

53
#define GPIO_PULL_NONE_UP_DN_STATES 3
54

55
struct GpioPinData {
56
    /* 0 (reserved) - Pin output set 2 - GPIO pins 64-96 (reserved on Pi Zero)
57
     * 1 (data[0]) - Pin output set 0 - GPIO pins 0-31
58
     * 2 (data[1])- Pin output set 1 - GPIO pins 32-53 (54-63 not connected/reserved)
59
     */
60
    volatile uint32_t reserved;
61
    volatile uint32_t data[BCM283X_GPIO_PORTS_COUNT];  
62
};
63

64
struct GpioRegs {
65
    volatile uint32_t func_select[3 * BCM283X_GPIO_PORTS_COUNT];
66
    struct GpioPinData output_set;              // Set as output
67
    struct GpioPinData output_clear;            // Set as input, is default
68
    struct GpioPinData level;                   // Read for value of pin
69
    struct GpioPinData ev_detect_status;        // Edge event detected
70
    struct GpioPinData re_detect_enable;        // Synchoronous rising edge event
71
    struct GpioPinData fe_detect_enable;        // Synchoronous falling edge event
72
    struct GpioPinData hi_detect_enable;        // Hi state detected
73
    struct GpioPinData lo_detect_enable;        // Lo state detected
74
    struct GpioPinData re_async_detect_enable;  // Enable async rising detection in ev_detect_status
75
                                                //     (no sampling by clock cycle for edge detection)
76
    struct GpioPinData fe_async_detect_enable;  // Enable async falling detection in ev_detect_status
77
    volatile uint32_t reserved;
78
    volatile uint32_t pupd_enable;              // See func bcm283x_gpio_pin_pullupdown() below
79
    volatile uint32_t pupd_enable_clocks[BCM283X_GPIO_PORTS_COUNT];
80
};
81

82
#define REGS_GPIO ((struct GpioRegs *)(PBASE))
83

84
//// Globals
85
//
86
static uint32_t _pinMaskPortA[GPIO_PULL_NONE_UP_DN_STATES];
87
static uint32_t _pinMaskPortB[GPIO_PULL_NONE_UP_DN_STATES];
88
//
89
////
90

91
// Assign desired pull state for one port+pin location, clear the other states for that port+pin
92
static void bcm293x_gpio_assign_pullup(GpioPullUpDn pull, unsigned char port, gpio_mask_t pin) {
93
    uint32_t *pullPorts[BCM283X_GPIO_PORTS_COUNT] = {_pinMaskPortA, _pinMaskPortB};
94
    GpioPullUpDn stateSelection[GPIO_PULL_NONE_UP_DN_STATES];
95
    switch(pull) {
96
        case GPUDUp:
97
            stateSelection[0] = GPUDUp;
98
            stateSelection[1] = GPUDDown;
99
            stateSelection[2] = GPUDNone;
100
        break;
101
        case GPUDDown:
102
            stateSelection[0] = GPUDDown;
103
            stateSelection[1] = GPUDNone;
104
            stateSelection[2] = GPUDUp;
105
        break;
106
        case GPUDNone:
107
            stateSelection[0] = GPUDNone;
108
            stateSelection[1] = GPUDUp;
109
            stateSelection[2] = GPUDDown;
110
        break;
111
    }
112
    pullPorts[port][stateSelection[0]] |= (1 << pin);
113
    pullPorts[port][stateSelection[1]] &= ~(1 << pin);
114
    pullPorts[port][stateSelection[2]] &= ~(1 << pin);
115
}
116

117
///
118
/// The function is set for every pin that has high value set on port
119
///
120
static void bcm283x_gpio_pin_set_func(unsigned char port, gpio_mask_t pins, GpioFunc func) {
121
    uint32_t pinNumber, selector;
122
    uint8_t bitStart, reg;
123

124
    for(unsigned short r = 0; r < REG_BITS; r++ ) {
125
        if( (pins >> r) & 0x01 ) {  // If bit is set, apply function
126
            pinNumber = PORT_PIN_TO_PINNUMBER(port, r);
127
            bitStart = (pinNumber * 3) % 30;
128
            reg = pinNumber / 10;
129

130
            selector = REGS_GPIO->func_select[reg];
131
            selector &= ~(7 << bitStart);
132
            selector |= (func << bitStart);
133

134
            REGS_GPIO->func_select[reg] = selector;
135
        }
136
    }
137
}
138

139
/// This change must be applied accross all pins in one action.  The state can't be read back. 
140
/// The change persists over a power on/off cycle.
141
static void bcm283x_gpio_pin_pullupdown(GpioPullUpDn pullState, uint32_t pinMaskPortA, uint32_t pinMaskPortB) {
142
    // Setup state to change to
143
    REGS_GPIO->pupd_enable = pullState;
144
    delay(150);
145
    // Indicate pins to change
146
    REGS_GPIO->pupd_enable_clocks[GPIO_PORT_A] = pinMaskPortA;
147
    REGS_GPIO->pupd_enable_clocks[GPIO_PORT_B] = pinMaskPortB;
148
    delay(150);
149
    // Clear values to indicate change complete
150
    REGS_GPIO->pupd_enable = 0;
151
    REGS_GPIO->pupd_enable_clocks[GPIO_PORT_A] = 0;
152
    REGS_GPIO->pupd_enable_clocks[GPIO_PORT_B] = 0;
153
}
154

155
///
156
/// port = port A or B
157
/// pins = a bit field for that port
158
/// mode = a bit field indicating mode of operation
159
///
160
static int bcm283x_gpio_setup_mode(unsigned char port, gpio_mask_t pins, int mode) {
161
    assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
162

163
    // Determine GPIO Function of Input/Output/Alternate X
164
    if(mode & GPIO_MODE_OUT_ALTERNATE) {
165
        bcm283x_gpio_pin_set_func(port,pins,GPIO_ALTERNATE_INVERT(mode));
166
    } else if (mode & (GPIO_MODE_IN 
167
        | GPIO_MODE_IN_PULL_UP | GPIO_MODE_IN_PULL_DOWN 
168
        | GPIO_MODE_IN_SCHMITT 
169
        | GPIO_MODE_INT_MODE_LEVEL0 | GPIO_MODE_INT_MODE_LEVEL1
170
        | GPIO_MODE_INT_MODE_RISING | GPIO_MODE_INT_MODE_FALLING )) {
171
        bcm283x_gpio_pin_set_func(port,pins,GFInput);        
172
    } else {
173
        bcm283x_gpio_pin_set_func(port,pins,GFOutput);        
174
    }
175

176
    // Synchornous rising edge
177
    if( mode & GPIO_MODE_INT_MODE_RISING ) {
178
        REGS_GPIO->re_detect_enable.data[port] |= pins;
179
        REGS_GPIO->ev_detect_status.data[port] |= pins;   
180
    } else {
181
        REGS_GPIO->re_detect_enable.data[port] &= ~pins;
182
    }
183

184
    // Synchornous falling edge
185
    if( mode & GPIO_MODE_INT_MODE_FALLING ) {
186
        REGS_GPIO->fe_detect_enable.data[port] |= pins;
187
        REGS_GPIO->ev_detect_status.data[port] |= pins;        
188
    } else {
189
        REGS_GPIO->fe_detect_enable.data[port] &= ~pins;
190
    }
191

192
    // Level Lo
193
    if(  mode & GPIO_MODE_INT_MODE_LEVEL0 ) {
194
        REGS_GPIO->lo_detect_enable.data[port] |= pins;
195
        REGS_GPIO->ev_detect_status.data[port] |= pins;        
196
    } else {
197
        REGS_GPIO->lo_detect_enable.data[port] &= ~pins;
198
    }
199

200
    // Level Hi
201
    if(  mode & GPIO_MODE_INT_MODE_LEVEL1 ) {
202
        REGS_GPIO->hi_detect_enable.data[port] |= pins;
203
        REGS_GPIO->ev_detect_status.data[port] |= pins;        
204
    } else {
205
        REGS_GPIO->hi_detect_enable.data[port] &= ~pins;
206
    }
207

208
    // Async mode = no sampleing, synch = sampleing 
209
    // (kind of like a schmitt trigger in that debouncing happens)
210
    if(mode & GPIO_MODE_IN_SCHMITT && mode & GPIO_MODE_INT_MODE_RISING) {
211
        REGS_GPIO->re_async_detect_enable.data[port] &= ~pins;
212
    } else {
213
        REGS_GPIO->re_async_detect_enable.data[port] |= pins;
214
    }
215

216
    // Async mode = no sampleing, synch = sampleing 
217
    // (kind of like a schmitt trigger in that debouncing happens)
218
    if(mode & GPIO_MODE_IN_SCHMITT && mode & GPIO_MODE_INT_MODE_FALLING) {
219
        REGS_GPIO->fe_async_detect_enable.data[port] &= ~pins;
220
    } else {
221
        REGS_GPIO->fe_async_detect_enable.data[port] |= pins;
222
    }
223

224
    // If no interrupt is requested, disable event interrupts
225
    //
226
    if(!(  mode & GPIO_MODE_INT_MODE_LEVEL1 
227
        || mode & GPIO_MODE_INT_MODE_LEVEL0 
228
        || mode & GPIO_MODE_INT_MODE_RISING
229
        || mode & GPIO_MODE_INT_MODE_FALLING)) {
230
        REGS_GPIO->ev_detect_status.data[port] &= ~pins;
231
        REGS_GPIO->fe_async_detect_enable.data[port] &= ~pins;
232
        REGS_GPIO->re_async_detect_enable.data[port] &= ~pins;
233
    }
234

235
    // Assign pull up / pull down -- floating isn't assigned as it is default state
236
    if(mode & (GPIO_MODE_IN_PULL_UP | GPIO_MODE_OUT_PUSH_PULL) ) {
237
        bcm293x_gpio_assign_pullup(GPUDUp, port, pins); 
238
    }
239
    if(mode & (GPIO_MODE_IN_PULL_DOWN | GPIO_MODE_VDD_LEVEL) ) {
240
        bcm293x_gpio_assign_pullup(GPUDDown, port, pins); 
241
    }
242
    
243
    // Assign pull up/down/float for all pins in one go (only way allowed)
244
    for( GpioPullUpDn i = 0; i < GPIO_PULL_NONE_UP_DN_STATES; i++) {
245
        bcm283x_gpio_pin_pullupdown(i, _pinMaskPortA[i], _pinMaskPortB[i]);
246
    }
247

248
    return 0;
249
}
250

251
static void bcm283x_gpio_set(unsigned char port, gpio_mask_t pins, char level) {
252
    assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
253
    
254
    if (level) {
255
        REGS_GPIO->output_set.data[port] |= pins;
256
    } else {
257
        REGS_GPIO->output_clear.data[port] |= pins;
258
    }
259
}
260

261
static gpio_mask_t bcm283x_gpio_get(unsigned char port, gpio_mask_t pins) {
262
    assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
263

264
    return REGS_GPIO->level.data[port] & pins;
265
}
266

267
static struct gpio_chip bcm283x_gpio_chip = {
268
    .setup_mode = bcm283x_gpio_setup_mode,
269
    .get = bcm283x_gpio_get,
270
    .set = bcm283x_gpio_set,
271
    .nports = BCM283X_GPIO_PORTS_COUNT
272
};
273

274
irq_return_t bcm283x_gpio_irq_handler(unsigned int irq_nr, void *data) {
275
    uint16_t ports[BCM283X_GPIO_PORTS_COUNT] = {0,0};
276

277
    ports[0] = (IRQ_NR_PORTA == irq_nr || IRQ_NR_ALL == irq_nr );
278
    ports[1] = (IRQ_NR_PORTB == irq_nr || IRQ_NR_ALL == irq_nr );
279

280
    for(uint16_t port = 0; port < BCM283X_GPIO_PORTS_COUNT; port++) {
281
        if(ports[port]) {
282
            uint32_t pins = REGS_GPIO->ev_detect_status.data[port];
283

284
            // Clear the interrupt event on GPIO port
285
            REGS_GPIO->ev_detect_status.data[port] = 0xFFFFFFFF;
286

287
            gpio_handle_irq(&bcm283x_gpio_chip, port, pins);            
288
        }
289
    }
290

291
    return IRQ_HANDLED;
292
}
293

294
static int bcm283x_gpio_init(void) {
295
    int res;
296

297
    // We don't know the pull up/down state of GPIO pins
298
    // so we'll set the change state to a default assignment.
299
    // It is expected that pin PINNUMBER_APPLY_CONFIG will be set at 
300
    // end of config in kernel! 
301
    uint32_t a[GPIO_PULL_NONE_UP_DN_STATES] = {BCM283X_PULL_PORTA_DEFLT_FLOAT, BCM283X_PULL_PORTA_DEFLT_DOWN, BCM283X_PULL_PORTA_DEFLT_UP};
302
    uint32_t b[GPIO_PULL_NONE_UP_DN_STATES] = {BCM283X_PULL_PORTB_DEFLT_FLOAT, BCM283X_PULL_PORTB_DEFLT_DOWN, BCM283X_PULL_PORTB_DEFLT_UP};
303

304
    for( GpioPullUpDn i = GPUDNone; i < GPIO_PULL_NONE_UP_DN_STATES; i++ ) {
305
        _pinMaskPortA[i] = a[i];
306
        _pinMaskPortB[i] = b[i];
307
    }
308

309
    res = irq_attach(GPU_IRQ, bcm283x_gpio_irq_handler, 0, NULL, "BCM283x GPIO irq handler");
310
    if (res >= 0) {
311
        res = gpio_register_chip(&bcm283x_gpio_chip, BCM283X_GPIO_CHIP_ID);
312
    }
313
    return res;
314
};
315

316
EMBOX_UNIT_INIT(bcm283x_gpio_init);
317

318
PERIPH_MEMORY_DEFINE(bcm283x_gpio, PBASE, sizeof(struct GpioRegs));
319

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.