diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-06-05 00:12:01 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-06-06 11:06:14 +0000 |
commit | e85bc74b307365e9a7b4adab51d646638db12fbd (patch) | |
tree | c45ba9079344b5cc0ea48a77b6aa77aacd71cdc5 /firmware/target/mips/ingenic_x1000/gpio-x1000.h | |
parent | 695d1701cdd1bb4539f652c2204f7787097b2715 (diff) | |
download | rockbox-e85bc74b307365e9a7b4adab51d646638db12fbd.tar.gz rockbox-e85bc74b307365e9a7b4adab51d646638db12fbd.zip |
x1000: GPIO refactor
The GPIO API was pretty clunky and pin settings were decentralized,
making it hard to see what was happening and making GPIO stuff look
like a mess, frankly.
Instead of passing clunky (port, pin) pairs everywhere, GPIOs are now
identified with a single int. The extra overhead should be minimal as
GPIO configuration is generally not on a performance-critical path.
Pin assignments are now mostly consolidated in gpio-target.h and put
in various tables so gpio_init() can assign most pins at boot time.
Most drivers no longer need to touch GPIOs and basic pin I/O stuff
can happen without config since pins are put into the right state.
IRQ pins still need to be configured manually before use.
Change-Id: Ic5326284b0b2a2f613e9e76a41cb50e24af3aa47
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/gpio-x1000.h')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/gpio-x1000.h | 164 |
1 files changed, 116 insertions, 48 deletions
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.h b/firmware/target/mips/ingenic_x1000/gpio-x1000.h index cfbe86338a..5d147fc18f 100644 --- a/firmware/target/mips/ingenic_x1000/gpio-x1000.h +++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.h | |||
@@ -22,33 +22,6 @@ | |||
22 | #ifndef __GPIO_X1000_H__ | 22 | #ifndef __GPIO_X1000_H__ |
23 | #define __GPIO_X1000_H__ | 23 | #define __GPIO_X1000_H__ |
24 | 24 | ||
25 | /* GPIO API | ||
26 | * -------- | ||
27 | * | ||
28 | * To assign a new function to a GPIO, call gpio_config(). This uses the | ||
29 | * hardware's GPIO Z facility to atomically set most GPIO registers at once, | ||
30 | * so it can be used to make any state transition safely. Since GPIO Z is | ||
31 | * a global hardware resource, it is unsafe to call gpio_config() from IRQ | ||
32 | * context -- if the interrupted code was also running gpio_config(), then | ||
33 | * the results would be unpredictable. | ||
34 | * | ||
35 | * Depending on the current GPIO state, certain state transitions are safe to | ||
36 | * perform without locking, as they only change one register: | ||
37 | * | ||
38 | * - for pins in GPIO_OUTPUT state: | ||
39 | * - use gpio_out_level() to change the output level | ||
40 | * | ||
41 | * - for pins in GPIO_IRQ_LEVEL or GPIO_IRQ_EDGE state: | ||
42 | * - use gpio_irq_level() to change the trigger level | ||
43 | * - use gpio_irq_mask() to mask/unmask the IRQ | ||
44 | * | ||
45 | * - for pins in GPIO_DEVICE or GPIO_INPUT state: | ||
46 | * - no special transitions allowed | ||
47 | * | ||
48 | * - in all states: | ||
49 | * - use gpio_set_pull() to change the pull-up/pull-down state | ||
50 | */ | ||
51 | |||
52 | #include "x1000/gpio.h" | 25 | #include "x1000/gpio.h" |
53 | 26 | ||
54 | /* GPIO port numbers */ | 27 | /* GPIO port numbers */ |
@@ -66,42 +39,137 @@ | |||
66 | #define GPIO_F_PAT0 1 | 39 | #define GPIO_F_PAT0 1 |
67 | 40 | ||
68 | /* GPIO function numbers */ | 41 | /* GPIO function numbers */ |
69 | #define GPIO_DEVICE(i) ((i)&3) | 42 | #define GPIOF_DEVICE(i) ((i)&3) |
70 | #define GPIO_OUTPUT(i) (0x4|((i)&1)) | 43 | #define GPIOF_OUTPUT(i) (0x4|((i)&1)) |
71 | #define GPIO_INPUT 0x16 | 44 | #define GPIOF_INPUT 0x16 |
72 | #define GPIO_IRQ_LEVEL(i) (0x1c|((i)&1)) | 45 | #define GPIOF_IRQ_LEVEL(i) (0x1c|((i)&1)) |
73 | #define GPIO_IRQ_EDGE(i) (0x1e|((i)&1)) | 46 | #define GPIOF_IRQ_EDGE(i) (0x1e|((i)&1)) |
47 | |||
48 | /* GPIO pin numbers */ | ||
49 | #define GPION_CREATE(port, pin) ((((port) & 3) << 5) | ((pin) & 0x1f)) | ||
50 | #define GPION_PORT(gpio) (((gpio) >> 5) & 3) | ||
51 | #define GPION_PIN(gpio) ((gpio) & 0x1f) | ||
52 | #define GPION_MASK(gpio) (1u << GPION_PIN(gpio)) | ||
53 | |||
54 | /* Easy pin number macros */ | ||
55 | #define GPIO_PA(x) GPION_CREATE(GPIO_A, x) | ||
56 | #define GPIO_PB(x) GPION_CREATE(GPIO_B, x) | ||
57 | #define GPIO_PC(x) GPION_CREATE(GPIO_C, x) | ||
58 | #define GPIO_PD(x) GPION_CREATE(GPIO_D, x) | ||
59 | |||
60 | /* Pingroup settings are used for system devices */ | ||
61 | struct pingroup_setting { | ||
62 | int port; | ||
63 | uint32_t pins; | ||
64 | int func; | ||
65 | }; | ||
66 | |||
67 | /* GPIO settings are used for single pins under software control */ | ||
68 | struct gpio_setting { | ||
69 | int gpio; | ||
70 | int func; | ||
71 | }; | ||
72 | |||
73 | /* Target pins are defined as GPIO_XXX constants usable with the GPIO API */ | ||
74 | enum { | ||
75 | #define DEFINE_GPIO(_name, _gpio, _func) GPIO_##_name = _gpio, | ||
76 | #define DEFINE_PINGROUP(...) | ||
77 | #include "gpio-target.h" | ||
78 | #undef DEFINE_GPIO | ||
79 | #undef DEFINE_PINGROUP | ||
80 | GPIO_NONE = -1, | ||
81 | }; | ||
82 | |||
83 | /* These are pin IDs which index gpio_settings */ | ||
84 | enum { | ||
85 | #define DEFINE_GPIO(_name, ...) PIN_##_name, | ||
86 | #define DEFINE_PINGROUP(...) | ||
87 | #include "gpio-target.h" | ||
88 | #undef DEFINE_GPIO | ||
89 | #undef DEFINE_PINGROUP | ||
90 | PIN_COUNT, | ||
91 | }; | ||
92 | |||
93 | /* Pingroup IDs which index pingroup_settings */ | ||
94 | enum { | ||
95 | #define DEFINE_GPIO(...) | ||
96 | #define DEFINE_PINGROUP(_name, ...) PINGROUP_##_name, | ||
97 | #include "gpio-target.h" | ||
98 | #undef DEFINE_GPIO | ||
99 | #undef DEFINE_PINGROUP | ||
100 | PINGROUP_COUNT, | ||
101 | }; | ||
102 | |||
103 | /* arrays which define the target's GPIO settings */ | ||
104 | extern const struct gpio_setting gpio_settings[PIN_COUNT]; | ||
105 | extern const struct pingroup_setting pingroup_settings[PINGROUP_COUNT]; | ||
74 | 106 | ||
107 | /* stringified names for use in debug menus */ | ||
108 | extern const char* const gpio_names[PIN_COUNT]; | ||
109 | extern const char* const pingroup_names[PINGROUP_COUNT]; | ||
110 | |||
111 | /* called at early init to set up GPIOs */ | ||
75 | extern void gpio_init(void); | 112 | extern void gpio_init(void); |
76 | extern void gpio_config(int port, unsigned pinmask, int func); | ||
77 | 113 | ||
78 | static inline void gpio_out_level(int port, unsigned pinmask, int level) | 114 | /* Use GPIO Z to reconfigure several pins atomically */ |
115 | extern void gpioz_configure(int port, uint32_t pins, int func); | ||
116 | |||
117 | static inline void gpio_set_function(int gpio, int func) | ||
118 | { | ||
119 | gpioz_configure(GPION_PORT(gpio), GPION_MASK(gpio), func); | ||
120 | } | ||
121 | |||
122 | static inline int gpio_get_level(int gpio) | ||
123 | { | ||
124 | return REG_GPIO_PIN(GPION_PORT(gpio)) & GPION_MASK(gpio) ? 1 : 0; | ||
125 | } | ||
126 | |||
127 | static inline void gpio_set_level(int gpio, int value) | ||
79 | { | 128 | { |
80 | if(level) | 129 | if(value) |
81 | jz_set(GPIO_PAT0(port), pinmask); | 130 | jz_set(GPIO_PAT0(GPION_PORT(gpio)), GPION_MASK(gpio)); |
82 | else | 131 | else |
83 | jz_clr(GPIO_PAT0(port), pinmask); | 132 | jz_clr(GPIO_PAT0(GPION_PORT(gpio)), GPION_MASK(gpio)); |
84 | } | 133 | } |
85 | 134 | ||
86 | #define gpio_irq_level gpio_out_level | 135 | static inline void gpio_set_pull(int gpio, int state) |
136 | { | ||
137 | if(state) | ||
138 | jz_set(GPIO_PULL(GPION_PORT(gpio)), GPION_MASK(gpio)); | ||
139 | else | ||
140 | jz_clr(GPIO_PULL(GPION_PORT(gpio)), GPION_MASK(gpio)); | ||
141 | } | ||
87 | 142 | ||
88 | static inline void gpio_irq_mask(int port, unsigned pinmask, int masked) | 143 | static inline void gpio_mask_irq(int gpio, int mask) |
89 | { | 144 | { |
90 | if(masked) | 145 | if(mask) |
91 | jz_set(GPIO_MSK(port), pinmask); | 146 | jz_set(GPIO_MSK(GPION_PORT(gpio)), GPION_MASK(gpio)); |
92 | else | 147 | else |
93 | jz_clr(GPIO_MSK(port), pinmask); | 148 | jz_clr(GPIO_MSK(GPION_PORT(gpio)), GPION_MASK(gpio)); |
94 | } | 149 | } |
95 | 150 | ||
96 | #define gpio_enable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 0) | 151 | #define gpio_set_irq_level gpio_set_level |
97 | #define gpio_disable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 1) | 152 | #define gpio_enable_irq(gpio) gpio_mask_irq((gpio), 0) |
153 | #define gpio_disable_irq(gpio) gpio_mask_irq((gpio), 1) | ||
98 | 154 | ||
99 | static inline void gpio_set_pull(int port, unsigned pinmask, int state) | 155 | /* Helper function for edge-triggered IRQs when you want to get an |
156 | * interrupt on both the rising and falling edges. The hardware can | ||
157 | * only be set up to interrupt on one edge, so interrupt handlers | ||
158 | * can call this function to flip the trigger to the other edge. | ||
159 | * | ||
160 | * Despite the name, this doesn't depend on the currently set edge, | ||
161 | * it just reads the GPIO state and sets up an edge trigger to detect | ||
162 | * a change to the other state -- if some transitions were missed the | ||
163 | * IRQ trigger may remain unchanged. | ||
164 | * | ||
165 | * It can be safely used to initialize the IRQ level. | ||
166 | */ | ||
167 | static inline void gpio_flip_edge_irq(int gpio) | ||
100 | { | 168 | { |
101 | if(state) | 169 | if(gpio_get_level(gpio)) |
102 | jz_set(GPIO_PULL(port), pinmask); | 170 | gpio_set_irq_level(gpio, 0); |
103 | else | 171 | else |
104 | jz_clr(GPIO_PULL(port), pinmask); | 172 | gpio_set_irq_level(gpio, 1); |
105 | } | 173 | } |
106 | 174 | ||
107 | #endif /* __GPIO_X1000_H__ */ | 175 | #endif /* __GPIO_X1000_H__ */ |