diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2012-01-02 18:32:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2012-01-02 18:32:35 +0000 |
commit | 5a8da163c842b08c6dbf1df6921507ec2fd5a534 (patch) | |
tree | 559e1704efa126f603411fd71abd99a01c0e6610 /firmware/target/arm/imx31/mc13783-imx31.c | |
parent | 1f0e6530386e2295d9573f3f9cb7fd75f2e87450 (diff) | |
download | rockbox-5a8da163c842b08c6dbf1df6921507ec2fd5a534.tar.gz rockbox-5a8da163c842b08c6dbf1df6921507ec2fd5a534.zip |
i.MX31 - Dethreading operations continue
Dispense with "pmic" thread and process PMIC events directly within ISR. Add
sense bit reading as part of the handling.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31528 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/mc13783-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/mc13783-imx31.c | 210 |
1 files changed, 104 insertions, 106 deletions
diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c index 01690f0def..31b91b46fc 100644 --- a/firmware/target/arm/imx31/mc13783-imx31.c +++ b/firmware/target/arm/imx31/mc13783-imx31.c | |||
@@ -26,26 +26,6 @@ | |||
26 | #include "debug.h" | 26 | #include "debug.h" |
27 | #include "kernel.h" | 27 | #include "kernel.h" |
28 | 28 | ||
29 | extern const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS]; | ||
30 | extern struct spi_node mc13783_spi; | ||
31 | |||
32 | /* PMIC event service data */ | ||
33 | static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; | ||
34 | static const char * const mc13783_thread_name = "pmic"; | ||
35 | static struct semaphore mc13783_svc_wake; | ||
36 | |||
37 | /* Tracking for which interrupts are enabled */ | ||
38 | static uint32_t pmic_int_enabled[2] = | ||
39 | { 0x00000000, 0x00000000 }; | ||
40 | |||
41 | static const unsigned char pmic_intm_regs[2] = | ||
42 | { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 }; | ||
43 | |||
44 | static const unsigned char pmic_ints_regs[2] = | ||
45 | { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 }; | ||
46 | |||
47 | static volatile unsigned int mc13783_thread_id = 0; | ||
48 | |||
49 | /* Extend the basic SPI transfer descriptor with our own fields */ | 29 | /* Extend the basic SPI transfer descriptor with our own fields */ |
50 | struct mc13783_transfer_desc | 30 | struct mc13783_transfer_desc |
51 | { | 31 | { |
@@ -57,6 +37,32 @@ struct mc13783_transfer_desc | |||
57 | }; | 37 | }; |
58 | }; | 38 | }; |
59 | 39 | ||
40 | extern const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS]; | ||
41 | extern struct spi_node mc13783_spi; | ||
42 | |||
43 | static uint32_t pmic_int_enb[2]; /* Enabled ints */ | ||
44 | static uint32_t pmic_int_sense_enb[2]; /* Enabled sense reading */ | ||
45 | static uint32_t int_pnd_buf[2]; /* Pending ints */ | ||
46 | static uint32_t int_data_buf[4]; /* ISR data buffer */ | ||
47 | static struct spi_transfer_desc int_xfers[2]; /* ISR transfer descriptor */ | ||
48 | static bool restore_event = true; | ||
49 | |||
50 | static inline bool mc13783_transfer(struct spi_transfer_desc *xfer, | ||
51 | uint32_t *txbuf, | ||
52 | uint32_t *rxbuf, | ||
53 | int count, | ||
54 | spi_transfer_cb_fn_type callback) | ||
55 | { | ||
56 | xfer->node = &mc13783_spi; | ||
57 | xfer->txbuf = txbuf; | ||
58 | xfer->rxbuf = rxbuf; | ||
59 | xfer->count = count; | ||
60 | xfer->callback = callback; | ||
61 | xfer->next = NULL; | ||
62 | |||
63 | return spi_transfer(xfer); | ||
64 | } | ||
65 | |||
60 | /* Called when a transfer is finished and data is ready/written */ | 66 | /* Called when a transfer is finished and data is ready/written */ |
61 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) | 67 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) |
62 | { | 68 | { |
@@ -69,74 +75,87 @@ static inline bool wait_for_transfer_complete(struct mc13783_transfer_desc *xfer | |||
69 | == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0; | 75 | == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0; |
70 | } | 76 | } |
71 | 77 | ||
72 | static void mc13783_interrupt_thread(void) | 78 | /* Efficient interrupt status and acking */ |
79 | static void mc13783_int_svc_complete_callback(struct spi_transfer_desc *xfer) | ||
73 | { | 80 | { |
74 | uint32_t pending[2]; | 81 | /* Restore PMIC interrupt events */ |
75 | 82 | if (restore_event) | |
76 | /* Enable mc13783 GPIO event */ | 83 | bitset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); |
77 | gpio_enable_event(MC13783_EVENT_ID); | ||
78 | 84 | ||
79 | while (1) | 85 | /* Call handlers */ |
86 | for ( | ||
87 | const struct mc13783_event *event = mc13783_events; | ||
88 | int_pnd_buf[0] | int_pnd_buf[1]; | ||
89 | event++ | ||
90 | ) | ||
80 | { | 91 | { |
81 | const struct mc13783_event *event, *event_last; | 92 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
82 | 93 | uint32_t pnd = int_pnd_buf[set]; | |
83 | semaphore_wait(&mc13783_svc_wake, TIMEOUT_BLOCK); | 94 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); |
84 | |||
85 | if (mc13783_thread_id == 0) | ||
86 | break; | ||
87 | |||
88 | mc13783_read_regs(pmic_ints_regs, pending, 2); | ||
89 | |||
90 | /* Only clear interrupts being dispatched */ | ||
91 | pending[0] &= pmic_int_enabled[0]; | ||
92 | pending[1] &= pmic_int_enabled[1]; | ||
93 | 95 | ||
94 | mc13783_write_regs(pmic_ints_regs, pending, 2); | 96 | if (pnd & mask) |
97 | { | ||
98 | event->callback(); | ||
99 | int_pnd_buf[set] = pnd & ~mask; | ||
100 | } | ||
101 | } | ||
95 | 102 | ||
96 | /* Whatever is going to be serviced in this loop has been | 103 | (void)xfer; |
97 | * acknowledged. Reenable interrupt and if anything was still | 104 | } |
98 | * pending or became pending again, another signal will be | ||
99 | * generated. */ | ||
100 | bitset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); | ||
101 | 105 | ||
102 | event = mc13783_events; | 106 | static void mc13783_int_svc_callback(struct spi_transfer_desc *xfer) |
103 | event_last = event + MC13783_NUM_EVENTS; | 107 | { |
108 | /* Only clear interrupts with handlers */ | ||
109 | int_pnd_buf[0] &= pmic_int_enb[0]; | ||
110 | int_pnd_buf[1] &= pmic_int_enb[1]; | ||
104 | 111 | ||
105 | /* .count is surely expected to be > 0 */ | 112 | /* Only read sense if enabled interrupts have them enabled */ |
106 | do | 113 | if ((int_pnd_buf[0] & pmic_int_sense_enb[0]) || |
107 | { | 114 | (int_pnd_buf[1] & pmic_int_sense_enb[1])) |
108 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; | 115 | { |
109 | uint32_t pnd = pending[set]; | 116 | int_data_buf[2] = MC13783_INTERRUPT_SENSE0 << 25; |
110 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); | 117 | int_data_buf[3] = MC13783_INTERRUPT_SENSE1 << 25; |
111 | 118 | int_xfers[1].rxbuf = int_data_buf; | |
112 | if (pnd & mask) | 119 | int_xfers[1].count = 4; |
113 | { | ||
114 | event->callback(); | ||
115 | pending[set] = pnd & ~mask; | ||
116 | } | ||
117 | |||
118 | if ((pending[0] | pending[1]) == 0) | ||
119 | break; /* Terminate early if nothing more to service */ | ||
120 | } | ||
121 | while (++event < event_last); | ||
122 | } | 120 | } |
123 | 121 | ||
124 | gpio_disable_event(MC13783_EVENT_ID); | 122 | /* Setup the write packets with status(es) to clear */ |
123 | int_data_buf[0] = (1 << 31) | (MC13783_INTERRUPT_STATUS0 << 25) | ||
124 | | int_pnd_buf[0]; | ||
125 | int_data_buf[1] = (1 << 31) | (MC13783_INTERRUPT_STATUS1 << 25) | ||
126 | | int_pnd_buf[1]; | ||
127 | (void)xfer; | ||
125 | } | 128 | } |
126 | 129 | ||
127 | /* GPIO interrupt handler for mc13783 */ | 130 | /* GPIO interrupt handler for mc13783 */ |
128 | void mc13783_event(void) | 131 | void mc13783_event(void) |
129 | { | 132 | { |
130 | /* Mask the interrupt (unmasked when PMIC thread services it). */ | 133 | /* Mask the interrupt (unmasked after final read services it). */ |
131 | bitclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); | 134 | bitclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); |
132 | MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); | 135 | MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); |
133 | semaphore_release(&mc13783_svc_wake); | 136 | |
137 | /* Setup the read packets */ | ||
138 | int_pnd_buf[0] = MC13783_INTERRUPT_STATUS0 << 25; | ||
139 | int_pnd_buf[1] = MC13783_INTERRUPT_STATUS1 << 25; | ||
140 | |||
141 | unsigned long cpsr = disable_irq_save(); | ||
142 | |||
143 | /* Do these without intervening transfers */ | ||
144 | if (mc13783_transfer(&int_xfers[0], int_pnd_buf, int_pnd_buf, 2, | ||
145 | mc13783_int_svc_callback)) | ||
146 | { | ||
147 | /* Start this provisionally and fill-in actual values during the | ||
148 | first transfer's callback - set whatever could be known */ | ||
149 | mc13783_transfer(&int_xfers[1], int_data_buf, NULL, 2, | ||
150 | mc13783_int_svc_complete_callback); | ||
151 | } | ||
152 | |||
153 | restore_irq(cpsr); | ||
134 | } | 154 | } |
135 | 155 | ||
136 | void INIT_ATTR mc13783_init(void) | 156 | void INIT_ATTR mc13783_init(void) |
137 | { | 157 | { |
138 | /* Serial interface must have been initialized first! */ | 158 | /* Serial interface must have been initialized first! */ |
139 | semaphore_init(&mc13783_svc_wake, 1, 0); | ||
140 | 159 | ||
141 | /* Enable the PMIC SPI module */ | 160 | /* Enable the PMIC SPI module */ |
142 | spi_enable_node(&mc13783_spi, true); | 161 | spi_enable_node(&mc13783_spi, true); |
@@ -147,62 +166,41 @@ void INIT_ATTR mc13783_init(void) | |||
147 | mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); | 166 | mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); |
148 | 167 | ||
149 | MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); | 168 | MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); |
150 | 169 | gpio_enable_event(MC13783_EVENT_ID); | |
151 | mc13783_thread_id = | ||
152 | create_thread(mc13783_interrupt_thread, | ||
153 | mc13783_thread_stack, sizeof(mc13783_thread_stack), 0, | ||
154 | mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); | ||
155 | } | 170 | } |
156 | 171 | ||
157 | void mc13783_close(void) | 172 | void mc13783_close(void) |
158 | { | 173 | { |
159 | unsigned int thread_id = mc13783_thread_id; | 174 | gpio_disable_event(MC13783_EVENT_ID); |
160 | |||
161 | if (thread_id == 0) | ||
162 | return; | ||
163 | |||
164 | mc13783_thread_id = 0; | ||
165 | semaphore_release(&mc13783_svc_wake); | ||
166 | thread_wait(thread_id); | ||
167 | spi_enable_node(&mc13783_spi, false); | 175 | spi_enable_node(&mc13783_spi, false); |
168 | } | 176 | } |
169 | 177 | ||
170 | bool mc13783_enable_event(enum mc13783_event_ids id) | 178 | void mc13783_enable_event(enum mc13783_event_ids id, bool enable) |
171 | { | 179 | { |
180 | static const unsigned char pmic_intm_regs[2] = | ||
181 | { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 }; | ||
182 | |||
172 | const struct mc13783_event * const event = &mc13783_events[id]; | 183 | const struct mc13783_event * const event = &mc13783_events[id]; |
173 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; | 184 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
174 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); | 185 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); |
175 | 186 | ||
176 | pmic_int_enabled[set] |= mask; | 187 | /* Mask GPIO while changing bits around */ |
177 | mc13783_clear(pmic_intm_regs[set], mask); | 188 | restore_event = false; |
178 | 189 | bitclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); | |
179 | return true; | 190 | mc13783_write_masked(pmic_intm_regs[set], |
191 | enable ? 0 : mask, mask); | ||
192 | bitmod32(&pmic_int_enb[set], enable ? mask : 0, mask); | ||
193 | bitmod32(&pmic_int_sense_enb[set], enable ? event->sense : 0, | ||
194 | event->sense); | ||
195 | restore_event = true; | ||
196 | bitset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); | ||
180 | } | 197 | } |
181 | 198 | ||
182 | void mc13783_disable_event(enum mc13783_event_ids id) | 199 | uint32_t mc13783_event_sense(enum mc13783_event_ids id) |
183 | { | 200 | { |
184 | const struct mc13783_event * const event = &mc13783_events[id]; | 201 | const struct mc13783_event * const event = &mc13783_events[id]; |
185 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; | 202 | unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; |
186 | uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); | 203 | return int_data_buf[2 + set] & event->sense; |
187 | |||
188 | pmic_int_enabled[set] &= ~mask; | ||
189 | mc13783_set(pmic_intm_regs[set], mask); | ||
190 | } | ||
191 | |||
192 | static inline bool mc13783_transfer(struct spi_transfer_desc *xfer, | ||
193 | uint32_t *txbuf, | ||
194 | uint32_t *rxbuf, | ||
195 | int count, | ||
196 | spi_transfer_cb_fn_type callback) | ||
197 | { | ||
198 | xfer->node = &mc13783_spi; | ||
199 | xfer->txbuf = txbuf; | ||
200 | xfer->rxbuf = rxbuf; | ||
201 | xfer->count = count; | ||
202 | xfer->callback = callback; | ||
203 | xfer->next = NULL; | ||
204 | |||
205 | return spi_transfer(xfer); | ||
206 | } | 204 | } |
207 | 205 | ||
208 | uint32_t mc13783_set(unsigned address, uint32_t bits) | 206 | uint32_t mc13783_set(unsigned address, uint32_t bits) |