summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-01-02 18:32:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2012-01-02 18:32:35 +0000
commit5a8da163c842b08c6dbf1df6921507ec2fd5a534 (patch)
tree559e1704efa126f603411fd71abd99a01c0e6610 /firmware
parent1f0e6530386e2295d9573f3f9cb7fd75f2e87450 (diff)
downloadrockbox-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')
-rw-r--r--firmware/export/config/gigabeats.h2
-rw-r--r--firmware/export/mc13783.h9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c4
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c33
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c4
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c15
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c31
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c17
-rw-r--r--firmware/target/arm/imx31/mc13783-imx31.c210
9 files changed, 168 insertions, 157 deletions
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h
index 481a666b4b..685076bfa3 100644
--- a/firmware/export/config/gigabeats.h
+++ b/firmware/export/config/gigabeats.h
@@ -164,7 +164,7 @@
164#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) 164#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS)
165 165
166/* Define this if target has an additional number of threads specific to it */ 166/* Define this if target has an additional number of threads specific to it */
167#define TARGET_EXTRA_THREADS 2 167#define TARGET_EXTRA_THREADS 1
168 168
169/* Type of mobile power - check this out */ 169/* Type of mobile power - check this out */
170#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ 170#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */
diff --git a/firmware/export/mc13783.h b/firmware/export/mc13783.h
index 99fd004420..4324d06df5 100644
--- a/firmware/export/mc13783.h
+++ b/firmware/export/mc13783.h
@@ -1333,7 +1333,8 @@ enum mc13783_int_ids
1333 1333
1334struct mc13783_event 1334struct mc13783_event
1335{ 1335{
1336 enum mc13783_int_ids int_id; 1336 enum mc13783_int_ids int_id : 8;
1337 uint32_t sense : 24;
1337 void (*callback)(void); 1338 void (*callback)(void);
1338}; 1339};
1339 1340
@@ -1343,7 +1344,9 @@ struct mc13783_event_list
1343 const struct mc13783_event *events; 1344 const struct mc13783_event *events;
1344}; 1345};
1345 1346
1346bool mc13783_enable_event(enum mc13783_event_ids event); 1347void mc13783_enable_event(enum mc13783_event_ids id, bool enable);
1347void mc13783_disable_event(enum mc13783_event_ids event); 1348
1349/* Read the sense bit if one exists - valid only within event handlers */
1350uint32_t mc13783_event_sense(enum mc13783_event_ids id);
1348 1351
1349#endif /* _MC13783_H_ */ 1352#endif /* _MC13783_H_ */
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
index eb30919077..b46fc2f63f 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
@@ -110,7 +110,7 @@ bool adc_enable_channel(int channel, bool enable)
110 != MC13783_DATA_ERROR; 110 != MC13783_DATA_ERROR;
111} 111}
112 112
113/* Called by mc13783 interrupt thread when conversion is complete */ 113/* ADC conversion complete event - called from PMIC ISR */
114void adc_done(void) 114void adc_done(void)
115{ 115{
116 semaphore_release(&adc_done_signal); 116 semaphore_release(&adc_done_signal);
@@ -132,5 +132,5 @@ void adc_init(void)
132 132
133 /* Enable ADCDONE event */ 133 /* Enable ADCDONE event */
134 mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_ADCDONEI); 134 mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_ADCDONEI);
135 mc13783_enable_event(MC13783_ADCDONE_EVENT); 135 mc13783_enable_event(MC13783_ADCDONE_EVENT, true);
136} 136}
diff --git a/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c
index 90db00dcc3..11d1d5a0b4 100644
--- a/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c
@@ -35,7 +35,7 @@
35static bool initialized = false; 35static bool initialized = false;
36#endif 36#endif
37 37
38static int ext_btn = BUTTON_NONE; /* Buttons not on KPP */ 38static unsigned long ext_btn = BUTTON_NONE; /* Buttons not on KPP */
39static bool hold_button = false; 39static bool hold_button = false;
40#ifndef BOOTLOADER 40#ifndef BOOTLOADER
41static bool hold_button_old = false; 41static bool hold_button_old = false;
@@ -150,24 +150,16 @@ int button_read_device(void)
150#endif 150#endif
151} 151}
152 152
153/* This is called from the mc13783 interrupt thread */ 153/* Helper to update the power button status */
154void button_power_event(void) 154static void power_button_update(bool pressed)
155{ 155{
156 bool pressed = 156 bitmod32(&ext_btn, pressed ? BUTTON_POWER : 0, BUTTON_POWER);
157 (mc13783_read(MC13783_INTERRUPT_SENSE1) & MC13783_ONOFD1S) == 0; 157}
158
159 int oldlevel = disable_irq_save();
160
161 if (pressed)
162 {
163 ext_btn |= BUTTON_POWER;
164 }
165 else
166 {
167 ext_btn &= ~BUTTON_POWER;
168 }
169 158
170 restore_irq(oldlevel); 159/* Power button event - called from PMIC ISR */
160void button_power_event(void)
161{
162 power_button_update(!mc13783_event_sense(MC13783_ONOFD1_EVENT));
171} 163}
172 164
173void button_init_device(void) 165void button_init_device(void)
@@ -203,8 +195,9 @@ void button_init_device(void)
203 * 6. Set the KDIE control bit bit. */ 195 * 6. Set the KDIE control bit bit. */
204 KPP_KPSR = KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKD; 196 KPP_KPSR = KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKD;
205 197
206 button_power_event(); 198 power_button_update(!(mc13783_read(MC13783_INTERRUPT_SENSE1)
207 mc13783_enable_event(MC13783_ONOFD1_EVENT); 199 & MC13783_ONOFD1S));
200 mc13783_enable_event(MC13783_ONOFD1_EVENT, true);
208 201
209#ifdef HAVE_HEADPHONE_DETECTION 202#ifdef HAVE_HEADPHONE_DETECTION
210 headphone_init(); 203 headphone_init();
@@ -220,7 +213,7 @@ void button_close_device(void)
220 /* Assumes HP detection is not available */ 213 /* Assumes HP detection is not available */
221 initialized = false; 214 initialized = false;
222 215
223 mc13783_disable_event(MC13783_ONOFD1_EVENT); 216 mc13783_enable_event(MC13783_ONOFD1_EVENT, true);
224 ext_btn = BUTTON_NONE; 217 ext_btn = BUTTON_NONE;
225} 218}
226#endif /* BUTTON_DRIVER_CLOSE */ 219#endif /* BUTTON_DRIVER_CLOSE */
diff --git a/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c
index 6e76615308..4e1792d467 100644
--- a/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c
@@ -171,7 +171,7 @@ static void headphone_thread(void)
171 } 171 }
172} 172}
173 173
174/* This is called from the mc13783 interrupt thread */ 174/* HP plugged/unplugged event - called from PMIC ISR */
175void headphone_detect_event(void) 175void headphone_detect_event(void)
176{ 176{
177 /* Trigger the thread immediately. */ 177 /* Trigger the thread immediately. */
@@ -197,5 +197,5 @@ void INIT_ATTR headphone_init(void)
197 197
198 /* Initially poll and then enable PMIC event */ 198 /* Initially poll and then enable PMIC event */
199 headphone_detect_event(); 199 headphone_detect_event();
200 mc13783_enable_event(MC13783_ONOFD2_EVENT); 200 mc13783_enable_event(MC13783_ONOFD2_EVENT, true);
201} 201}
diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
index 12009fae06..6ae8c23c48 100644
--- a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
@@ -55,28 +55,33 @@ const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS] =
55{ 55{
56 [MC13783_ADCDONE_EVENT] = /* ADC conversion complete */ 56 [MC13783_ADCDONE_EVENT] = /* ADC conversion complete */
57 { 57 {
58 .int_id = MC13783_INT_ID_ADCDONE, 58 .int_id = MC13783_INT_ID_ADCDONE,
59 .sense = 0,
59 .callback = adc_done, 60 .callback = adc_done,
60 }, 61 },
61 [MC13783_ONOFD1_EVENT] = /* Power button */ 62 [MC13783_ONOFD1_EVENT] = /* Power button */
62 { 63 {
63 .int_id = MC13783_INT_ID_ONOFD1, 64 .int_id = MC13783_INT_ID_ONOFD1,
65 .sense = MC13783_ONOFD1S,
64 .callback = button_power_event, 66 .callback = button_power_event,
65 }, 67 },
66 [MC13783_SE1_EVENT] = /* Main charger detection */ 68 [MC13783_SE1_EVENT] = /* Main charger detection */
67 { 69 {
68 .int_id = MC13783_INT_ID_SE1, 70 .int_id = MC13783_INT_ID_SE1,
71 .sense = MC13783_SE1S,
69 .callback = charger_main_detect_event, 72 .callback = charger_main_detect_event,
70 }, 73 },
71 [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */ 74 [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */
72 { 75 {
73 .int_id = MC13783_INT_ID_USB, 76 .int_id = MC13783_INT_ID_USB,
77 .sense = MC13783_USB4V4S,
74 .callback = usb_connect_event, 78 .callback = usb_connect_event,
75 }, 79 },
76#ifdef HAVE_HEADPHONE_DETECTION 80#ifdef HAVE_HEADPHONE_DETECTION
77 [MC13783_ONOFD2_EVENT] = /* Headphone jack */ 81 [MC13783_ONOFD2_EVENT] = /* Headphone jack */
78 { 82 {
79 .int_id = MC13783_INT_ID_ONOFD2, 83 .int_id = MC13783_INT_ID_ONOFD2,
84 .sense = 0,
80 .callback = headphone_detect_event, 85 .callback = headphone_detect_event,
81 }, 86 },
82#endif 87#endif
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
index 11276a6c3a..5b255a0e27 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
@@ -33,7 +33,7 @@
33#include "fmradio_i2c.h" 33#include "fmradio_i2c.h"
34#endif 34#endif
35 35
36static unsigned int power_status = POWER_INPUT_NONE; 36static unsigned long power_status = POWER_INPUT_NONE;
37 37
38/* Detect which power sources are present. */ 38/* Detect which power sources are present. */
39unsigned int power_input_status(void) 39unsigned int power_input_status(void)
@@ -58,24 +58,28 @@ void usb_charging_maxcurrent_change(int maxcurrent)
58 /* Nothing to do */ 58 /* Nothing to do */
59} 59}
60 60
61/* Detect changes in presence of the AC adaptor. */ 61/* Helper to update the charger status */
62static void update_main_charger(bool present)
63{
64 bitmod32(&power_status, present ? POWER_INPUT_MAIN_CHARGER : 0,
65 POWER_INPUT_MAIN_CHARGER);
66}
67
68/* Detect changes in presence of the AC adaptor. Called from PMIC ISR. */
62void charger_main_detect_event(void) 69void charger_main_detect_event(void)
63{ 70{
64 if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S) 71 update_main_charger(mc13783_event_sense(MC13783_INT_ID_SE1)
65 power_status |= POWER_INPUT_MAIN_CHARGER; 72 & MC13783_SE1S);
66 else
67 power_status &= ~POWER_INPUT_MAIN_CHARGER;
68} 73}
69 74
70/* Detect changes in USB bus power. Called from usb connect event handler. */ 75/* Detect changes in USB bus power. Called from usb connect event ISR. */
71void charger_usb_detect_event(int status) 76void charger_usb_detect_event(int status)
72{ 77{
73 /* USB plugged does not imply charging is possible or even 78 /* USB plugged does not imply charging is possible or even
74 * powering the device to maintain the battery. */ 79 * powering the device to maintain the battery. */
75 if (status == USB_INSERTED) 80 bitmod32(&power_status,
76 power_status |= POWER_INPUT_USB_CHARGER; 81 status == USB_INSERTED ? POWER_INPUT_USB_CHARGER : 0,
77 else 82 POWER_INPUT_USB_CHARGER);
78 power_status &= ~POWER_INPUT_USB_CHARGER;
79} 83}
80 84
81/* charging_state is implemented in powermgmt-imx31.c */ 85/* charging_state is implemented in powermgmt-imx31.c */
@@ -152,8 +156,9 @@ void power_init(void)
152#endif 156#endif
153 157
154 /* Poll initial state */ 158 /* Poll initial state */
155 charger_main_detect_event(); 159 update_main_charger(mc13783_read(MC13783_INTERRUPT_SENSE0)
160 & MC13783_SE1S);
156 161
157 /* Enable detect event */ 162 /* Enable detect event */
158 mc13783_enable_event(MC13783_SE1_EVENT); 163 mc13783_enable_event(MC13783_SE1_EVENT, true);
159} 164}
diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c
index b157544016..1584ffb574 100644
--- a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c
@@ -59,16 +59,23 @@ bool usb_plugged(void)
59 return mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_USB4V4S; 59 return mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_USB4V4S;
60} 60}
61 61
62void usb_connect_event(void) 62/* Helper to update the USB cable status */
63static void update_usb_status(bool sense)
63{ 64{
64 /* Read the immediate state of the cable from the PMIC */ 65 int status = sense ? USB_INSERTED : USB_EXTRACTED;
65 int status = usb_plugged() ? USB_INSERTED : USB_EXTRACTED;
66 usb_status = status; 66 usb_status = status;
67 /* Notify power that USB charging is potentially available */ 67 /* Notify power that USB charging is potentially available */
68 charger_usb_detect_event(status); 68 charger_usb_detect_event(status);
69 usb_status_event(status); 69 usb_status_event(status);
70} 70}
71 71
72/* Detect presence of USB bus - called from PMIC ISR */
73void usb_connect_event(void)
74{
75 /* Read the associated sense value */
76 update_usb_status(mc13783_event_sense(MC13783_USB_EVENT));
77}
78
72int usb_detect(void) 79int usb_detect(void)
73{ 80{
74 return usb_status; 81 return usb_status;
@@ -80,10 +87,10 @@ void usb_init_device(void)
80 usb_drv_startup(); 87 usb_drv_startup();
81 88
82 /* Initially poll */ 89 /* Initially poll */
83 usb_connect_event(); 90 update_usb_status(usb_plugged());
84 91
85 /* Enable PMIC event */ 92 /* Enable PMIC event */
86 mc13783_enable_event(MC13783_USB_EVENT); 93 mc13783_enable_event(MC13783_USB_EVENT, true);
87} 94}
88 95
89void usb_enable(bool on) 96void usb_enable(bool on)
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)