aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-21 15:45:38 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-22 14:06:10 -0400
commit70f22b667a7d91c68d663c1bf9ef1c0bdcbdd377 (patch)
tree4cb7a320aa0dac59d38b00d6357f2df8073a7e23
parent3ba9c0ecd6fd881d21292a369f8b7e45be5d2cb4 (diff)
downloadlibrambutan-70f22b667a7d91c68d663c1bf9ef1c0bdcbdd377.tar.gz
librambutan-70f22b667a7d91c68d663c1bf9ef1c0bdcbdd377.zip
I2C: Move F1-only errata workarounds out of libmaple/i2c.c.
The IRQ priority hack is unnecessary on targets with properly functioning I2C IRQ handlers, so we shouldn't use it unless we have to. Add a mechanism so a series header can provide such a hack if necessary. Have the F1 series header use this mechanism. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
-rw-r--r--libmaple/i2c.c30
-rw-r--r--libmaple/include/libmaple/i2c.h18
-rw-r--r--libmaple/stm32f1/i2c.c36
-rw-r--r--libmaple/stm32f1/include/series/i2c.h3
4 files changed, 57 insertions, 30 deletions
diff --git a/libmaple/i2c.c b/libmaple/i2c.c
index 2e11f54..291bf16 100644
--- a/libmaple/i2c.c
+++ b/libmaple/i2c.c
@@ -223,36 +223,6 @@ void i2c_master_enable(i2c_dev *dev, uint32 flags) {
nvic_irq_enable(dev->er_nvic_line);
i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR);
- /*
- * Important STM32 Errata:
- *
- * See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8),
- * Section 2.11.1, 2.11.2.
- *
- * 2.11.1:
- * When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not
- * managed before the current byte is being transferred, problems may be
- * encountered such as receiving an extra byte, reading the same data twice
- * or missing data.
- *
- * 2.11.2:
- * In Master Receiver mode, when closing the communication using
- * method 2, the content of the last read data can be corrupted.
- *
- * If the user software is not able to read the data N-1 before the STOP
- * condition is generated on the bus, the content of the shift register
- * (data N) will be corrupted. (data N is shifted 1-bit to the left).
- *
- * ----------------------------------------------------------------------
- *
- * In order to ensure that events are not missed, the i2c interrupt must
- * not be preempted. We set the i2c interrupt priority to be the highest
- * interrupt in the system (priority level 0). All other interrupts have
- * been initialized to priority level 16. See nvic_init().
- */
- nvic_irq_set_priority(dev->ev_nvic_line, 0);
- nvic_irq_set_priority(dev->er_nvic_line, 0);
-
/* Make it go! */
i2c_peripheral_enable(dev);
diff --git a/libmaple/include/libmaple/i2c.h b/libmaple/include/libmaple/i2c.h
index 8ce6674..c66ebcb 100644
--- a/libmaple/include/libmaple/i2c.h
+++ b/libmaple/include/libmaple/i2c.h
@@ -50,6 +50,17 @@ extern "C" {
* - uint32 _i2c_bus_clk(i2c_dev*): Clock frequency of dev's bus, in
* MHz. (This is for internal use only).
*
+ * - (optional) _I2C_HAVE_IRQ_FIXUP: Leave undefined, or define to 1.
+ * This is for internal use only. It's a hack to work around a
+ * silicon bug related to I2C IRQ pre-emption on some targets. If 1,
+ * the series header must also declare and implement a routine with
+ * this signature (it may also be provided as a macro):
+ *
+ * void _i2c_irq_priority_fixup(i2c_dev*)
+ *
+ * This will be called by i2c_enable_irq() before actually enabling
+ * I2C interrupts.
+ *
* - Reg. map base pointers, device pointer declarations.
*/
@@ -243,6 +254,12 @@ static inline void i2c_stop_condition(i2c_dev *dev) {
/* IRQ enable/disable */
+#ifndef _I2C_HAVE_IRQ_FIXUP
+/* The series header provides this if _I2C_HAVE_IRQ_FIXUP is defined,
+ * but we need it either way. */
+#define _i2c_irq_priority_fixup(dev) ((void)0)
+#endif
+
#define I2C_IRQ_ERROR I2C_CR2_ITERREN
#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN
#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN
@@ -255,6 +272,7 @@ static inline void i2c_stop_condition(i2c_dev *dev) {
* I2C_IRQ_BUFFER (buffer interrupt).
*/
static inline void i2c_enable_irq(i2c_dev *dev, uint32 irqs) {
+ _i2c_irq_priority_fixup(dev);
dev->regs->CR2 |= irqs;
}
diff --git a/libmaple/stm32f1/i2c.c b/libmaple/stm32f1/i2c.c
index ed0ca97..4e918ad 100644
--- a/libmaple/stm32f1/i2c.c
+++ b/libmaple/stm32f1/i2c.c
@@ -74,3 +74,39 @@ void __irq_i2c1_er(void) {
void __irq_i2c2_er(void) {
_i2c_irq_error_handler(I2C2);
}
+
+/*
+ * Internal APIs
+ */
+
+void _i2c_irq_priority_fixup(i2c_dev *dev) {
+ /*
+ * Important STM32 Errata:
+ *
+ * See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8),
+ * Section 2.11.1, 2.11.2.
+ *
+ * 2.11.1:
+ * When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not
+ * managed before the current byte is being transferred, problems may be
+ * encountered such as receiving an extra byte, reading the same data twice
+ * or missing data.
+ *
+ * 2.11.2:
+ * In Master Receiver mode, when closing the communication using
+ * method 2, the content of the last read data can be corrupted.
+ *
+ * If the user software is not able to read the data N-1 before the STOP
+ * condition is generated on the bus, the content of the shift register
+ * (data N) will be corrupted. (data N is shifted 1-bit to the left).
+ *
+ * ----------------------------------------------------------------------
+ *
+ * In order to ensure that events are not missed, the i2c interrupt must
+ * not be preempted. We set the i2c interrupt priority to be the highest
+ * interrupt in the system (priority level 0). All other interrupts have
+ * been initialized to priority level 16. See nvic_init().
+ */
+ nvic_irq_set_priority(dev->ev_nvic_line, 0);
+ nvic_irq_set_priority(dev->er_nvic_line, 0);
+}
diff --git a/libmaple/stm32f1/include/series/i2c.h b/libmaple/stm32f1/include/series/i2c.h
index 0c89df4..315a7e3 100644
--- a/libmaple/stm32f1/include/series/i2c.h
+++ b/libmaple/stm32f1/include/series/i2c.h
@@ -63,4 +63,7 @@ static inline uint32 _i2c_bus_clk(i2c_dev *dev) {
return STM32_PCLK1 / (1000 * 1000);
}
+#define _I2C_HAVE_IRQ_FIXUP 1
+void _i2c_irq_priority_fixup(i2c_dev *dev);
+
#endif /* _LIBMAPLE_STM32F1_I2C_H_ */