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 | |
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
-rw-r--r-- | firmware/export/config/gigabeats.h | 2 | ||||
-rw-r--r-- | firmware/export/mc13783.h | 9 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c | 4 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c | 33 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c | 4 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c | 15 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c | 31 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c | 17 | ||||
-rw-r--r-- | firmware/target/arm/imx31/mc13783-imx31.c | 210 |
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 | ||
1334 | struct mc13783_event | 1334 | struct 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 | ||
1346 | bool mc13783_enable_event(enum mc13783_event_ids event); | 1347 | void mc13783_enable_event(enum mc13783_event_ids id, bool enable); |
1347 | void mc13783_disable_event(enum mc13783_event_ids event); | 1348 | |
1349 | /* Read the sense bit if one exists - valid only within event handlers */ | ||
1350 | uint32_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 */ |
114 | void adc_done(void) | 114 | void 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 @@ | |||
35 | static bool initialized = false; | 35 | static bool initialized = false; |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | static int ext_btn = BUTTON_NONE; /* Buttons not on KPP */ | 38 | static unsigned long ext_btn = BUTTON_NONE; /* Buttons not on KPP */ |
39 | static bool hold_button = false; | 39 | static bool hold_button = false; |
40 | #ifndef BOOTLOADER | 40 | #ifndef BOOTLOADER |
41 | static bool hold_button_old = false; | 41 | static 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 */ |
154 | void button_power_event(void) | 154 | static 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 */ |
160 | void button_power_event(void) | ||
161 | { | ||
162 | power_button_update(!mc13783_event_sense(MC13783_ONOFD1_EVENT)); | ||
171 | } | 163 | } |
172 | 164 | ||
173 | void button_init_device(void) | 165 | void 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 */ |
175 | void headphone_detect_event(void) | 175 | void 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 | ||
36 | static unsigned int power_status = POWER_INPUT_NONE; | 36 | static unsigned long power_status = POWER_INPUT_NONE; |
37 | 37 | ||
38 | /* Detect which power sources are present. */ | 38 | /* Detect which power sources are present. */ |
39 | unsigned int power_input_status(void) | 39 | unsigned 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 */ |
62 | static 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. */ | ||
62 | void charger_main_detect_event(void) | 69 | void 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. */ |
71 | void charger_usb_detect_event(int status) | 76 | void 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 | ||
62 | void usb_connect_event(void) | 62 | /* Helper to update the USB cable status */ |
63 | static 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 */ | ||
73 | void usb_connect_event(void) | ||
74 | { | ||
75 | /* Read the associated sense value */ | ||
76 | update_usb_status(mc13783_event_sense(MC13783_USB_EVENT)); | ||
77 | } | ||
78 | |||
72 | int usb_detect(void) | 79 | int 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 | ||
89 | void usb_enable(bool on) | 96 | void 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 | ||
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) |