Closed haukepetersen closed 6 years ago
just one remark, adxl345 driver is missing from the list.
@haukepetersen ads101x update: #9165
For stm32, we should try to come up with a unified driver. It seems that there are 2 different IPs:
So 2 versions of the driver should be written.
@vincent-d, +1 for unifying the drivers but it seems simpler/harmless to do it in a second step. Is it ok for you ?
I would say it's easier to do it straight. But of course, I would develop with only 1 arch first and adapt afterwards. But the current situation with a driver per sub-family where most of them are just copy/paste is crap.
Ok @vincent-d, you convinced me. See #9202 for the L0 and F3. I don't know how to cleanly set the internal speed options and how to unify with the second type of drivers (F1/F2/F4/L1). Are you ok to work on this part ?
So L0/F3 should work on F0/F7/L4 as well. And as it seems quite different for the others, the best would maybe to start with two different versions. I can help, but I'll be quite busy next week, so not sure if I'll have time :/ Thanks for starting that btw!
I will try to do the Kinetis implementation during the weekend
Great !! Many thanks @gebart !:+1:
Just FYI,
I am testing with the new i2c API and a samr21 board,
the new API says the return of i2c read/writes should be error codes ( I2C_ACK, I2C_ADDR_NACK...) but they are return the number of bytes.
Also the read bytes and read reg seem to be physically reading one too many: i2c_read_bytes(addr, 4, 0) actually reads 5 bytes (according to the oscilloscope) but says it reads 4.
the new API says the return of i2c read/writes should be error codes ( I2C_ACK, I2C_ADDR_NACK...) but they are return the number of bytes.
In fact, when we implemented the proposed I2C API, we also look at the current implementation of SPI. I guess the number of bytes read or written is borrowed from the SPI and we didn't take in account the I2C_ACK return code. Both solutions can be applied we just need a consensus I guess :)
Regarding the extra byte read, as I remember this issue is also true in the previous API. We need to ensure this issue only exists for SAM0 CPUs. I'll try to fix whenever I can but it can also be fix later if it's only impacting SAM0.
What about slave mode? This would be a good time to at least think about where slave mode will fit in the API. Since i2c is a multimaster bus, the API should be designed in a way that a device can act both as a master and a slave on the same bus.
What about slave mode?
TBH, I don't really think about it but you're right, this may be a good timing to start some discussions about like how the slave API will look like ? Regarding multimaster bus, I've personally never used it. Do we know if there are some existing, working and open-source implementations somewhere ? This may fit in another issue.
One more open issue: How do we configure timeout limits for when the slave holds the clock low for too long?
What about create a device attribute as params ? Or maybe add a hardcoded value per driver ?
@dylad, #9202 (STM32 I2C driver) has rebase issue with the new_i2c_if branch because #7658 (DMA) was merged after the embargo in master (Travis is trying to rebase against master) and it conflicts with #9202 since they touch the same files. I'm wondering what it the best strategy to solve this. Should we rebase new_i2c_if branch on top of master now ? or backport #7658 to new_i2c_if ? or something else ?
This is where the fun begins. I guess we should rebase the new_i2c_if branch. It's probably better than push a revert which will re-revert right ?
we should rebase the new_i2c_if branch
I would do that as well, because this is what will happen in the end. Maybe @miri64 will have some advice, since she already managed the netif feature branch.
If we do the rebase, the next question is: do we want to keep the merge commit in the rebased branch (using --preserve-merges) or not ?
do we want to keep the merge commit in the rebased branch (using --preserve-merges) or not ?
I'm afraid I don't understand a single word of your question... I'm not a git expert.
I'm not a git expert.
No problem.
The current RIOT workflow in GitHub creates merge commits each time one hits the GitHub green button.
So for example the new_i2c_if branch contains all merge commits created when we merged PRs containing updates of I2C API and drivers (have a look at the git history, merge commits have 2 ancestors commits - for other git experts, let's keep it simple for now).
But when one rebases a branch with merge commits (here new_i2c_if) on top of another branch (here it would be master), the default behaviour of git is to remove them. Instead, you can force git to try to also reapply the merge commits using the --preserve-merges
option.
Personally, I would vote for removing the merge commits (I don't like it, I prefer a linear history).
Well I guess I will leave this question to our zealots :) TBH I understand the situation but I don't have strong opinion.
Maybe @miri64 will have some advice, since she already managed the netif feature branch.
If you don't want to loose merge commits (and there identity) the way to go would be to merge master into the branch, but this should have been prevented by the embargo. So the cleanest way would be to revert or backport the commits that cause the conflict/are required.
So the cleanest way would be to revert or backport the commits that cause the conflict/are required
But if we revert something, we will need to re-revert it once we merge the whole branch right ? isn't weird ?
Thanks @miri64. Is it ok if we cherrypick commits from #7658 in new_i2c_if ?
But if we revert something, we will need to re-revert it once we merge the whole branch right ? isn't weird ?
Yes, but that's what we did for gnrc_netif/gnrc_ipv6_nib as well.
Thanks @miri64. Is it ok if we cherrypick commits from #7658 in new_i2c_if ?
I'd rather like you to open backport commits, so the history is trackable in both git and GitHub.
We need to take a decision about the I2C return code within the API. Should we return I2C_ACK (aka 0) or the number of bytes read/written ? I guess the latter may help when debugging. This is also the current behaviour of the SPI bus.
What do you think ?
+1 for the number of bytes
Ok after some debate with myself, I change my mind.I think we should drop the number of bytes read/written. I2C is synchronous bus. We're not supposed to read more bytes by design and if we read/write less, the function will return an error code like I2C_DATA_NACK or I2C_ERR. I also took another look to the SPI implementation. The behaviour is not that clear to me and return the return is not always used (some functions return void)
I will just put some things I noticed regarding that issue:
Adding error codes to the API is something trivial as we can implement them later in the CPU drivers.
The only time the number of bytes would differ from length is an error (maybe you can think of adding timeout and collision errors), meaning that the number of bytes should never return any non error that is not equal to length
Some driver already implements timeout or lost arbitration on the bus. So I guess it's probably more useful to improve our error codes. If something is wrong on the bus, we will return the dedicated error code and if everything is alright, what's the point on returning the number of bytes written or read as we already knew it and pass it as an argument ?
Dropping the return number of bytes will force us to make more changes in devices driver but this is for our sake right ? :)
If something is wrong on the bus, we will return the dedicated error code and if everything is alright, what's the point on returning the number of bytes written or read as we already knew it and pass it as an argument?
Agreed, I don't think there should ever be cases that we wouldn't get an error but would have a different number of bytes.
Dropping the return number of bytes will force us to make more changes in devices driver but this is for our sake right?
Heh, I didn't think of it like that but it makes sense. Most things use this as 0 is an error, now that it is success it will be easier to detect. I guess the question is try to keep some compatibility which might have small unknown errors or make it blatant and propagate changes everywhere.
RPi i2c c library seems to return the number of bytes (but I think it is because they use a filesystem write) but doesn't use error codes (so if error it a read will return 0)
I looked at the I2C api of other MCU OSes (Zephyr/MyNewt/Nuttx) and they all use return codes (0: success, negative values in case of errors, some use errno return codes). None are using the number of bytes read/written. So this goes into @dylad direction.
If we're agreed then we will take this direction. Using errno could be interesting but is this already used in RIOT ? I never saw it but it already covers 99% of use case. Most of the work is locate in the device driver. We can still add new I2C error code now (or after this refactoring) in the API, which is not harmful to any drivers and add support for this new error code in CPU driver. I will take a look at all currently implemented I2C driver today. Then we can discuss adding new error code (like timeout, lost arbitration etc.). If most of the previous CPU driver already support support it then we can add new error code, if not this will be done later as CPU driver improvement.
if you have any idea on useful error code, feel free to post it :) This idea is to use all of this to ease debugging.
Using errno could be interesting but is this already used in RIOT ?
I think netdev, can, saul apis are using them and there are occurrences in the native related code.
What about -EIO (IO errors), -ENXIO (no such device or address), -ETIMEDOUT ?
So the current I2C_ADDR_NACK will be replaced by -ENXIO but what about I2C_DATA_NACK ? replaced by -EIO ?
+1 +1 +1 to errno!
+1 for errno, but the common codes must be explained in the function docs (no data ACK, no addr ACK, timeout, and so on). Otherwise each cpu implementation is likely to use different codes for the same condition.
I think we need to rework the drivers/periph_common/i2c.c since the i2c_write_regs doesn't seem to support the typical i2c slave register access. Currently it is
int i2c_write_regs(i2c_t dev, uint16_t addr, uint16_t reg, const void *data, size_t len, uint8_t flags)
{
int8_t err;
/* First set ADDR and register with no stop */
err = i2c_write_bytes(dev, addr, ®, (flags & I2C_REG16) ? 2 : 1, flags & I2C_NOSTOP );
if (err < 0) {
return err;
}
/* Then write data to the device */
return i2c_write_bytes(dev, addr, data, len, flags);
}
which seems to do separate reads, what I think it should be is:
int i2c_write_regs(i2c_t dev, uint16_t addr, uint16_t reg, const void *data, size_t len, uint8_t flags)
{
int8_t err;
/* First set ADDR and register with no stop */
err = i2c_write_bytes(dev, addr, ®, (flags & I2C_REG16) ? 2 : 1, I2C_NOSTOP );
if (err < 0) {
return err;
}
/* Then write data to the device */
return i2c_write_bytes(dev, addr, data, len, I2C_NOSTART);
}
I don't think we should allow users to set I2C_NOSTART or I2C_NOSTOP flags, only the I2C_REG16 and maybe the I2C_ADDR10.
One of the requested features in a api rework is the ability to do repeated starts. There are weird devices which use multiple repeated starts as part of their protocol. I think the function you posted just needs to change flags & no stop to flags| no stop
@gebart is right, you are probably using an old version of the API because we saw this error and flags & I2C_NOSTOP
has been replaced by flags | I2C_NOSTOP
already.
I still think there may be an issue, what about if it was (flags | NOSTOP & ~NOSTART) since the first write must always write the start and the second write should either NOSTART for the majority of sensor or have a start in case of repeated start. Sound OK?
you are probably using an old version of the API
Changing so fast!
Changing so fast!
This is a fast growing child !
We can clear the NOSTART flag by default for the first call but I don't think we should change the second call. Don't forget theses functions are just here for basic, generic, and non-weird I2C sensors. If we implement weird I2C stuff later, the device driver will have to implement its own read/write function using i2c_read_bytes and i2c_write_bytes with the flags which give much more flexibility.
Can anyone think of a case where we would need I2C_NOSTOP or I2C_NOSTART with read_bytes command? If not than I think I got a bug free ;) solution that works with the SAMR21 and fits the API.
It would be good to have periph_init run after uart_stdio has been initialized. My debug prints in init are causing the system to crash because of uninitialized stdio... I'll remove them for now
@gebart I'm not against, in fact that would be logical. But this doesn't belong to the I2C refactoring as they are plenty of way for doing it. Maybe opening an issue is a better idea. I don't think all maintainers/developers are looking at this issue.
I take a look at the Linux kernel documentation -> here I propose to use the following errno's code : -EIO -> I2C_DATA_NACK ?, -ENXIO -> equivalent to the current I2C_ADDR_NACK, -ETIMEDOUT -> timeout occurs before device's response -EOPNOTSUPP -> use when a flag or operation is not supported by the driver -EAGAIN -> lost bus arbitration (when another master take the lead of the bus)
What do you think ? Did I miss a case ?
@dylad I have used -EINVAL in other places when there were invalid arguments passed to functions, such as flags. How about EINVAL instead of EOPNOTSUPP?
This is a bit late, but changing the CPU periph api into three primitives would make it possible to reuse a larger part of the logic as periph_common instead of reimplementing in each CPU. The primitives would be start, transmit, and receive. Where the transmit or receive functions would perform a stop unless a flag is sent. This way we could place all of the logic for 10 bit addressing inside periph_common and the cpu drivers would not need to bother with anything but normal 7 bit addresses in the start primitive. Slave mode would still need special implementations for 10 to bit mode on each CPU though.
I don't really see any reason why a CPU driver needs to implement the _regs functions, the periph_common implementation should be equivalent. The register address is only a few bytes at most and the i2c bus is slow, so there is little to gain from streamlining it all with a single DMA transfer instead of splitting it in two transfers.
@gebart Yes we can use -EINVAL for invalid argument like the len params. But I'm septic for the flags. returning an EINVAL for a flag that we define in our own API is just to weird for me.
In the current state, CPU implements must implements at least read_bytes and write_bytes which are basically the transmit and receive you were talking. I don't see a particular reason to export the start function outside of the CPU driver since we have enough control of this using the flags.
All others I2C API functions have default wrappers under drivers/periph_common/i2c.c that are #ifdef If the CPU want to use the default wrapper then the arch must define it (like the SPI bus in fact). You can have an example here.
If for a specific reason the CPU cannot use the default wrapper (hardware limitation ? Sillicon bug ? Weird IP ?) then CPU driver can redefine each functions. So right know CPU drivers don't need to implement _regs functions but they can do it if it's needed.
The changes proposed in #6575 and #6576 need many efforts to be undertaken. This issue will track the progress and should help to synchronize the work efforts.
Modules in RIOT that need adaption: CPUS:
DRIVERS AND TESTS:
#9432#9458)TESTS:
PACKAGES: