summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/mc13783-imx31.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31/mc13783-imx31.c')
-rw-r--r--firmware/target/arm/imx31/mc13783-imx31.c210
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
29extern const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS];
30extern struct spi_node mc13783_spi;
31
32/* PMIC event service data */
33static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)];
34static const char * const mc13783_thread_name = "pmic";
35static struct semaphore mc13783_svc_wake;
36
37/* Tracking for which interrupts are enabled */
38static uint32_t pmic_int_enabled[2] =
39 { 0x00000000, 0x00000000 };
40
41static const unsigned char pmic_intm_regs[2] =
42 { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 };
43
44static const unsigned char pmic_ints_regs[2] =
45 { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 };
46
47static 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 */
50struct mc13783_transfer_desc 30struct mc13783_transfer_desc
51{ 31{
@@ -57,6 +37,32 @@ struct mc13783_transfer_desc
57 }; 37 };
58}; 38};
59 39
40extern const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS];
41extern struct spi_node mc13783_spi;
42
43static uint32_t pmic_int_enb[2]; /* Enabled ints */
44static uint32_t pmic_int_sense_enb[2]; /* Enabled sense reading */
45static uint32_t int_pnd_buf[2]; /* Pending ints */
46static uint32_t int_data_buf[4]; /* ISR data buffer */
47static struct spi_transfer_desc int_xfers[2]; /* ISR transfer descriptor */
48static bool restore_event = true;
49
50static 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 */
61static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) 67static 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
72static void mc13783_interrupt_thread(void) 78/* Efficient interrupt status and acking */
79static 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; 106static 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 */
128void mc13783_event(void) 131void 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
136void INIT_ATTR mc13783_init(void) 156void 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
157void mc13783_close(void) 172void 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
170bool mc13783_enable_event(enum mc13783_event_ids id) 178void 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
182void mc13783_disable_event(enum mc13783_event_ids id) 199uint32_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
192static 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
208uint32_t mc13783_set(unsigned address, uint32_t bits) 206uint32_t mc13783_set(unsigned address, uint32_t bits)