diff options
| author | Siarhei B <[email protected]> | 2025-07-23 17:00:10 +0200 |
|---|---|---|
| committer | Siarhei B <[email protected]> | 2025-08-04 10:19:14 +0200 |
| commit | 917a509c1a899d7054f1a9cf2a21369dc143f46b (patch) | |
| tree | 3644bc7da1b55bd72a3e41788b2e7b635097a8cc /embassy-mspm0/src | |
| parent | 45852b852bf7623718f20ab9151655a417370655 (diff) | |
mspm0-I2C: automate source clock definition
- i2c-config: automatically defines clock source based on input I2C rate
- i2c: proper config functions naming
- i2c-examples: adapt to changed API
- i2c: save initialization pf cctr register
Diffstat (limited to 'embassy-mspm0/src')
| -rw-r--r-- | embassy-mspm0/src/i2c.rs | 120 |
1 files changed, 81 insertions, 39 deletions
diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index f99e02c59..d093a7e21 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs | |||
| @@ -25,7 +25,7 @@ use crate::Peri; | |||
| 25 | pub enum ClockSel { | 25 | pub enum ClockSel { |
| 26 | /// Use the bus clock. | 26 | /// Use the bus clock. |
| 27 | /// | 27 | /// |
| 28 | /// By default the BusClk runs at 32 MHz. | 28 | /// Configurable clock. |
| 29 | BusClk, | 29 | BusClk, |
| 30 | 30 | ||
| 31 | /// Use the middle frequency clock. | 31 | /// Use the middle frequency clock. |
| @@ -127,8 +127,15 @@ impl BusSpeed { | |||
| 127 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 127 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 128 | /// Config Error | 128 | /// Config Error |
| 129 | pub enum ConfigError { | 129 | pub enum ConfigError { |
| 130 | /// Invalid clock rate. | ||
| 131 | /// | ||
| 130 | /// The clock rate could not be configured with the given conifguratoin. | 132 | /// The clock rate could not be configured with the given conifguratoin. |
| 131 | InvalidClockRate, | 133 | InvalidClockRate, |
| 134 | |||
| 135 | /// Clock source not enabled. | ||
| 136 | /// | ||
| 137 | /// The clock soure is not enabled is SYSCTL. | ||
| 138 | ClockSourceNotEnabled, | ||
| 132 | } | 139 | } |
| 133 | 140 | ||
| 134 | #[non_exhaustive] | 141 | #[non_exhaustive] |
| @@ -136,7 +143,7 @@ pub enum ConfigError { | |||
| 136 | /// Config | 143 | /// Config |
| 137 | pub struct Config { | 144 | pub struct Config { |
| 138 | /// I2C clock source. | 145 | /// I2C clock source. |
| 139 | pub clock_source: ClockSel, | 146 | clock_source: ClockSel, |
| 140 | 147 | ||
| 141 | /// I2C clock divider. | 148 | /// I2C clock divider. |
| 142 | pub clock_div: ClockDiv, | 149 | pub clock_div: ClockDiv, |
| @@ -160,7 +167,7 @@ pub struct Config { | |||
| 160 | impl Default for Config { | 167 | impl Default for Config { |
| 161 | fn default() -> Self { | 168 | fn default() -> Self { |
| 162 | Self { | 169 | Self { |
| 163 | clock_source: ClockSel::BusClk, | 170 | clock_source: ClockSel::MfClk, |
| 164 | clock_div: ClockDiv::DivBy1, | 171 | clock_div: ClockDiv::DivBy1, |
| 165 | invert_sda: false, | 172 | invert_sda: false, |
| 166 | invert_scl: false, | 173 | invert_scl: false, |
| @@ -178,7 +185,7 @@ impl Config { | |||
| 178 | pub fn scl_pf(&self) -> PfType { | 185 | pub fn scl_pf(&self) -> PfType { |
| 179 | PfType::input(self.scl_pull, self.invert_scl) | 186 | PfType::input(self.scl_pull, self.invert_scl) |
| 180 | } | 187 | } |
| 181 | fn timer_period(&self) -> u8 { | 188 | fn calculate_timer_period(&self) -> u8 { |
| 182 | // Sets the timer period to bring the clock frequency to the selected I2C speed | 189 | // Sets the timer period to bring the clock frequency to the selected I2C speed |
| 183 | // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where: | 190 | // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where: |
| 184 | // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV) | 191 | // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV) |
| @@ -186,13 +193,13 @@ impl Config { | |||
| 186 | // - SCL_LP is the SCL Low period (fixed at 6) | 193 | // - SCL_LP is the SCL Low period (fixed at 6) |
| 187 | // - SCL_HP is the SCL High period (fixed at 4) | 194 | // - SCL_HP is the SCL High period (fixed at 4) |
| 188 | // - I2C_CLK is functional clock frequency | 195 | // - I2C_CLK is functional clock frequency |
| 189 | return (((self.calculate_clock_rate() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) | 196 | return (((self.calculate_clock_source() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) |
| 190 | .try_into() | 197 | .try_into() |
| 191 | .unwrap(); | 198 | .unwrap(); |
| 192 | } | 199 | } |
| 193 | 200 | ||
| 194 | #[cfg(any(mspm0c110x))] | 201 | #[cfg(any(mspm0c110x))] |
| 195 | pub fn calculate_clock_rate(&self) -> Hertz { | 202 | fn calculate_clock_source(&self) -> Hertz { |
| 196 | // Assume that BusClk has default value. | 203 | // Assume that BusClk has default value. |
| 197 | // TODO: calculate BusClk more precisely. | 204 | // TODO: calculate BusClk more precisely. |
| 198 | match self.clock_source { | 205 | match self.clock_source { |
| @@ -205,7 +212,7 @@ impl Config { | |||
| 205 | mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, | 212 | mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, |
| 206 | mspm0l134x, mspm0l222x | 213 | mspm0l134x, mspm0l222x |
| 207 | ))] | 214 | ))] |
| 208 | pub fn calculate_clock_rate(&self) -> Hertz { | 215 | fn calculate_clock_source(&self) -> Hertz { |
| 209 | // Assume that BusClk has default value. | 216 | // Assume that BusClk has default value. |
| 210 | // TODO: calculate BusClk more precisely. | 217 | // TODO: calculate BusClk more precisely. |
| 211 | match self.clock_source { | 218 | match self.clock_source { |
| @@ -214,16 +221,47 @@ impl Config { | |||
| 214 | } | 221 | } |
| 215 | } | 222 | } |
| 216 | 223 | ||
| 217 | pub fn check_clock_rate(&self) -> bool { | 224 | fn check_clock_i2c(&self) -> bool { |
| 218 | // make sure source clock is ~20 faster than i2c clock | 225 | // make sure source clock is ~20 faster than i2c clock |
| 219 | let clk_ratio = 20u8; | 226 | let clk_ratio = 20u8; |
| 220 | 227 | ||
| 221 | let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); | 228 | let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); |
| 222 | let src_clk = self.calculate_clock_rate(); | 229 | let src_clk = self.calculate_clock_source(); |
| 223 | 230 | ||
| 224 | // check clock rate | 231 | // check clock rate |
| 225 | return src_clk >= i2c_clk * clk_ratio; | 232 | return src_clk >= i2c_clk * clk_ratio; |
| 226 | } | 233 | } |
| 234 | |||
| 235 | fn define_clock_source(&mut self) -> bool { | ||
| 236 | // decide which clock source to choose based on i2c clock. | ||
| 237 | // If i2c speed <= 200kHz, use MfClk, otherwise use BusClk | ||
| 238 | if self.bus_speed.hertz() / self.clock_div.divider() > Hertz::khz(200) { | ||
| 239 | // TODO: check if BUSCLK enabled | ||
| 240 | self.clock_source = ClockSel::BusClk; | ||
| 241 | } else { | ||
| 242 | // is MFCLK enabled | ||
| 243 | if !pac::SYSCTL.mclkcfg().read().usemftick() { | ||
| 244 | return false; | ||
| 245 | } | ||
| 246 | self.clock_source = ClockSel::MfClk; | ||
| 247 | } | ||
| 248 | return true; | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Check the config. | ||
| 252 | /// | ||
| 253 | /// Make sure that configuration is valid and enabled by the system. | ||
| 254 | pub fn check_config(&mut self) -> Result<(), ConfigError> { | ||
| 255 | if !self.define_clock_source() { | ||
| 256 | return Err(ConfigError::ClockSourceNotEnabled); | ||
| 257 | } | ||
| 258 | |||
| 259 | if !self.check_clock_i2c() { | ||
| 260 | return Err(ConfigError::InvalidClockRate); | ||
| 261 | } | ||
| 262 | |||
| 263 | Ok(()) | ||
| 264 | } | ||
| 227 | } | 265 | } |
| 228 | 266 | ||
| 229 | /// Serial error | 267 | /// Serial error |
| @@ -288,7 +326,7 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> { | |||
| 288 | type ConfigError = ConfigError; | 326 | type ConfigError = ConfigError; |
| 289 | 327 | ||
| 290 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | 328 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { |
| 291 | self.set_config(config) | 329 | self.set_config(*config) |
| 292 | } | 330 | } |
| 293 | } | 331 | } |
| 294 | 332 | ||
| @@ -297,10 +335,10 @@ impl<'d> I2c<'d, Blocking> { | |||
| 297 | peri: Peri<'d, T>, | 335 | peri: Peri<'d, T>, |
| 298 | scl: Peri<'d, impl SclPin<T>>, | 336 | scl: Peri<'d, impl SclPin<T>>, |
| 299 | sda: Peri<'d, impl SdaPin<T>>, | 337 | sda: Peri<'d, impl SdaPin<T>>, |
| 300 | config: Config, | 338 | mut config: Config, |
| 301 | ) -> Result<Self, ConfigError> { | 339 | ) -> Result<Self, ConfigError> { |
| 302 | if !config.check_clock_rate() { | 340 | if let Err(err) = config.check_config() { |
| 303 | return Err(ConfigError::InvalidClockRate); | 341 | return Err(err); |
| 304 | } | 342 | } |
| 305 | 343 | ||
| 306 | Self::new_inner(peri, scl, sda, config) | 344 | Self::new_inner(peri, scl, sda, config) |
| @@ -313,10 +351,10 @@ impl<'d> I2c<'d, Async> { | |||
| 313 | scl: Peri<'d, impl SclPin<T>>, | 351 | scl: Peri<'d, impl SclPin<T>>, |
| 314 | sda: Peri<'d, impl SdaPin<T>>, | 352 | sda: Peri<'d, impl SdaPin<T>>, |
| 315 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 353 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 316 | config: Config, | 354 | mut config: Config, |
| 317 | ) -> Result<Self, ConfigError> { | 355 | ) -> Result<Self, ConfigError> { |
| 318 | if !config.check_clock_rate() { | 356 | if let Err(err) = config.check_config() { |
| 319 | return Err(ConfigError::InvalidClockRate); | 357 | return Err(err); |
| 320 | } | 358 | } |
| 321 | 359 | ||
| 322 | let i2c = Self::new_inner(peri, scl, sda, config); | 360 | let i2c = Self::new_inner(peri, scl, sda, config); |
| @@ -330,9 +368,9 @@ impl<'d> I2c<'d, Async> { | |||
| 330 | 368 | ||
| 331 | impl<'d, M: Mode> I2c<'d, M> { | 369 | impl<'d, M: Mode> I2c<'d, M> { |
| 332 | /// Reconfigure the driver | 370 | /// Reconfigure the driver |
| 333 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 371 | pub fn set_config(&mut self, mut config: Config) -> Result<(), ConfigError> { |
| 334 | if !config.check_clock_rate() { | 372 | if let Err(err) = config.check_config() { |
| 335 | return Err(ConfigError::InvalidClockRate); | 373 | return Err(err); |
| 336 | } | 374 | } |
| 337 | 375 | ||
| 338 | self.info.interrupt.disable(); | 376 | self.info.interrupt.disable(); |
| @@ -345,7 +383,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 345 | scl.update_pf(config.scl_pf()); | 383 | scl.update_pf(config.scl_pf()); |
| 346 | } | 384 | } |
| 347 | 385 | ||
| 348 | self.init(config) | 386 | self.init(&config) |
| 349 | } | 387 | } |
| 350 | 388 | ||
| 351 | fn init(&mut self, config: &Config) -> Result<(), ConfigError> { | 389 | fn init(&mut self, config: &Config) -> Result<(), ConfigError> { |
| @@ -370,21 +408,25 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 370 | }); | 408 | }); |
| 371 | 409 | ||
| 372 | // Reset controller transfer, follow TI example | 410 | // Reset controller transfer, follow TI example |
| 373 | self.info | 411 | self.info.regs.controller(0).cctr().modify(|w| { |
| 374 | .regs | 412 | w.set_burstrun(false); |
| 375 | .controller(0) | 413 | w.set_start(false); |
| 376 | .cctr() | 414 | w.set_stop(false); |
| 377 | .write_value(i2c::regs::Cctr::default()); | 415 | w.set_ack(false); |
| 416 | w.set_cackoen(false); | ||
| 417 | w.set_rd_on_txempty(false); | ||
| 418 | w.set_cblen(0); | ||
| 419 | }); | ||
| 378 | 420 | ||
| 379 | self.state | 421 | self.state |
| 380 | .clock | 422 | .clock |
| 381 | .store(config.calculate_clock_rate().0, Ordering::Relaxed); | 423 | .store(config.calculate_clock_source().0, Ordering::Relaxed); |
| 382 | 424 | ||
| 383 | self.info | 425 | self.info |
| 384 | .regs | 426 | .regs |
| 385 | .controller(0) | 427 | .controller(0) |
| 386 | .ctpr() | 428 | .ctpr() |
| 387 | .write(|w| w.set_tpr(config.timer_period())); | 429 | .write(|w| w.set_tpr(config.calculate_timer_period())); |
| 388 | 430 | ||
| 389 | // Set Tx Fifo threshold, follow TI example | 431 | // Set Tx Fifo threshold, follow TI example |
| 390 | self.info | 432 | self.info |
| @@ -1087,39 +1129,39 @@ mod tests { | |||
| 1087 | 1129 | ||
| 1088 | /// These tests are based on TI's reference caluclation. | 1130 | /// These tests are based on TI's reference caluclation. |
| 1089 | #[test] | 1131 | #[test] |
| 1090 | fn ti_timer_period() { | 1132 | fn ti_calculate_timer_period() { |
| 1091 | let mut config = Config::default(); | 1133 | let mut config = Config::default(); |
| 1092 | config.clock_div = ClockDiv::DivBy1; | 1134 | config.clock_div = ClockDiv::DivBy1; |
| 1093 | config.bus_speed = BusSpeed::FastMode; | 1135 | config.bus_speed = BusSpeed::FastMode; |
| 1094 | config.clock_source = ClockSel::BusClk; | 1136 | config.clock_source = ClockSel::BusClk; |
| 1095 | assert!(matches!(config.timer_period(), 7)); | 1137 | assert!(matches!(config.calculate_timer_period(), 7)); |
| 1096 | } | 1138 | } |
| 1097 | 1139 | ||
| 1098 | #[test] | 1140 | #[test] |
| 1099 | fn ti_timer_period_2() { | 1141 | fn ti_calculate_timer_period_2() { |
| 1100 | let mut config = Config::default(); | 1142 | let mut config = Config::default(); |
| 1101 | config.clock_div = ClockDiv::DivBy2; | 1143 | config.clock_div = ClockDiv::DivBy2; |
| 1102 | config.bus_speed = BusSpeed::FastMode; | 1144 | config.bus_speed = BusSpeed::FastMode; |
| 1103 | config.clock_source = ClockSel::BusClk; | 1145 | config.clock_source = ClockSel::BusClk; |
| 1104 | assert!(matches!(config.timer_period(), 3)); | 1146 | assert!(matches!(config.calculate_timer_period(), 3)); |
| 1105 | } | 1147 | } |
| 1106 | 1148 | ||
| 1107 | #[test] | 1149 | #[test] |
| 1108 | fn ti_timer_period_3() { | 1150 | fn ti_calculate_timer_period_3() { |
| 1109 | let mut config = Config::default(); | 1151 | let mut config = Config::default(); |
| 1110 | config.clock_div = ClockDiv::DivBy2; | 1152 | config.clock_div = ClockDiv::DivBy2; |
| 1111 | config.bus_speed = BusSpeed::Standard; | 1153 | config.bus_speed = BusSpeed::Standard; |
| 1112 | config.clock_source = ClockSel::BusClk; | 1154 | config.clock_source = ClockSel::BusClk; |
| 1113 | assert!(matches!(config.timer_period(), 15)); | 1155 | assert!(matches!(config.calculate_timer_period(), 15)); |
| 1114 | } | 1156 | } |
| 1115 | 1157 | ||
| 1116 | #[test] | 1158 | #[test] |
| 1117 | fn ti_timer_period_4() { | 1159 | fn ti_calculate_timer_period_4() { |
| 1118 | let mut config = Config::default(); | 1160 | let mut config = Config::default(); |
| 1119 | config.clock_div = ClockDiv::DivBy2; | 1161 | config.clock_div = ClockDiv::DivBy2; |
| 1120 | config.bus_speed = BusSpeed::Custom(100_000); | 1162 | config.bus_speed = BusSpeed::Custom(100_000); |
| 1121 | config.clock_source = ClockSel::BusClk; | 1163 | config.clock_source = ClockSel::BusClk; |
| 1122 | assert!(matches!(config.timer_period(), 15)); | 1164 | assert!(matches!(config.calculate_timer_period(), 15)); |
| 1123 | } | 1165 | } |
| 1124 | 1166 | ||
| 1125 | #[test] | 1167 | #[test] |
| @@ -1127,7 +1169,7 @@ mod tests { | |||
| 1127 | let mut config = Config::default(); | 1169 | let mut config = Config::default(); |
| 1128 | config.clock_source = ClockSel::BusClk; | 1170 | config.clock_source = ClockSel::BusClk; |
| 1129 | config.bus_speed = BusSpeed::FastModePlus; | 1171 | config.bus_speed = BusSpeed::FastModePlus; |
| 1130 | assert!(config.check_clock_rate()); | 1172 | assert!(config.check_clock_i2c()); |
| 1131 | } | 1173 | } |
| 1132 | 1174 | ||
| 1133 | #[test] | 1175 | #[test] |
| @@ -1135,7 +1177,7 @@ mod tests { | |||
| 1135 | let mut config = Config::default(); | 1177 | let mut config = Config::default(); |
| 1136 | config.clock_source = ClockSel::BusClk; | 1178 | config.clock_source = ClockSel::BusClk; |
| 1137 | config.bus_speed = BusSpeed::FastMode; | 1179 | config.bus_speed = BusSpeed::FastMode; |
| 1138 | assert!(config.check_clock_rate()); | 1180 | assert!(config.check_clock_i2c()); |
| 1139 | } | 1181 | } |
| 1140 | 1182 | ||
| 1141 | #[test] | 1183 | #[test] |
| @@ -1143,7 +1185,7 @@ mod tests { | |||
| 1143 | let mut config = Config::default(); | 1185 | let mut config = Config::default(); |
| 1144 | config.clock_source = ClockSel::MfClk; | 1186 | config.clock_source = ClockSel::MfClk; |
| 1145 | config.bus_speed = BusSpeed::FastModePlus; | 1187 | config.bus_speed = BusSpeed::FastModePlus; |
| 1146 | assert!(!config.check_clock_rate()); | 1188 | assert!(!config.check_clock_i2c()); |
| 1147 | } | 1189 | } |
| 1148 | 1190 | ||
| 1149 | #[test] | 1191 | #[test] |
| @@ -1151,6 +1193,6 @@ mod tests { | |||
| 1151 | let mut config = Config::default(); | 1193 | let mut config = Config::default(); |
| 1152 | config.clock_source = ClockSel::MfClk; | 1194 | config.clock_source = ClockSel::MfClk; |
| 1153 | config.bus_speed = BusSpeed::FastMode; | 1195 | config.bus_speed = BusSpeed::FastMode; |
| 1154 | assert!(!config.check_clock_rate()); | 1196 | assert!(!config.check_clock_i2c()); |
| 1155 | } | 1197 | } |
| 1156 | } | 1198 | } |
