Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/kinetis: reworked GPIO driver implementation #4830

Merged
merged 1 commit into from
Feb 29, 2016

Conversation

haukepetersen
Copy link
Contributor

NOTE: this is untested code and so far only compiles for the mulle board

alternative to #4813

Compared to master, this PR saves ~200 byte ROM and ~200 byte RAM while offering slightly more functionality (-> gpio_init_af)

I think there is no need for an explicit port module, all GPIO pin/port related functions can be put into this one implementation file, while still offering the AF pin configuration to be used by other peripheral modules.

@haukepetersen haukepetersen added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation Platform: ARM Platform: This PR/issue effects ARM-based platforms State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet labels Feb 16, 2016
* @brief Define a CPU specific GPIO pin generator macro
*/
#define GPIO_PIN(port, pin) ((port << GPIO_PORT_SHIFT) | pin)
#define GPIO_PIN(x, y) (((x + 1) << 12) | (x << 6) | y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit of not staying on byte boundaries? Are you trying to save bits?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple: by encoding the pins like this, it is very easy to generage the base registers for PORTx and GPIOx. Just look at the port() and `gpio()' getter functions to see the beauty of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand now, but I think we need at least an explanation inside a code comment.
However, do we really need to have both x and x+1 inside the pin value? Would it not make more sense to just do + 1 inside the port function below?

@jnohlgard
Copy link
Member

I know this is WIP but I don't like the general look of the code, there is too much bit-fiddling and confusing multiplications with array indexes etc.

Can we do this in steps? I'll refactor the PORT PR #4813 to merge port.c into gpio.c and change the name from port_init to gpio_init_af and then we can start looking at refactoring the whole GPIO driver?

@jnohlgard
Copy link
Member

What I mean is that I'm sure there are some good thoughts in this PR, but there are too many changes at once to be able to follow and review..

@haukepetersen
Copy link
Contributor Author

but there are too many changes at once to be able to follow and review..

I disagree. Especially for an GPIO driver the testing is very simple ans straight forward. Just run the test/periph_gpio application and test a number of pins. If they work the code is alright, if not I need to fix stuff. The testing is essentially the same than testing a new GPIO implementation.

In general, the used coding style is proved itself for many other boards and is pretty much the most efficient option we have at the moment.

@haukepetersen haukepetersen removed the State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet label Feb 18, 2016
@haukepetersen
Copy link
Contributor Author

Now not WIP anymore, successfully tested with the user button and LED on the phytec board for now. Needs to be tested for the FRDM and the mulle next.

@jnohlgard
Copy link
Member

jnohlgard commented Feb 18, 2016 via email

@haukepetersen
Copy link
Contributor Author

I thought about what you said earlier and had an idea: how about I cut the interrupt channel lookup part out of the driver and put it in it's own (shared) module? For this I can then write unit-tests and it also can easily be re-used in other GPIO drivers. Does this sound better to you?

@jnohlgard
Copy link
Member

Good idea, other GPIO drivers will likely benefit from the same kind of interrupt handler pool to reduce memory bloat when few GPIOs are configured as interrupt sources.

@jnohlgard jnohlgard added this to the Release 2016.03 milestone Feb 19, 2016
#define GPIO_PORTS_NUMOF (7)

/**
* @brief We allow for 7 (4-bit - 1) concurrent external interrupts to be set
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(3 bit - 1)

2**4 = 16,
2**3 = 8

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ups, beginners mistake :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though 7 does not break anything, as long as the number is <= 8.

@haukepetersen haukepetersen force-pushed the opt_kinetis_gpio branch 2 times, most recently from 058e3a7 to 7aff094 Compare February 19, 2016 16:20
@haukepetersen
Copy link
Contributor Author

Did one fix that got lost on the way, forgot to clear an interrupt channel again if a pin is redefined from interrupt input to simple input or output. While at it, I found a nice simplification that does not need the 'isr_map' to be initialized with ones anymore, also allowing us now to have 16 interrupts configured concurrently.

@jnohlgard
Copy link
Member

Nice, I like simplifications


return ret;
static inline PORT_Type *port(gpio_t pin)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and all other static functions: Could you add a prefix underscore to the name to follow the conventions used in other modules (e.g. gnrc, xtimer)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, I would rather see the other modules to not do it - it is not really a convention and the underscore style is IMHO very ugly...

@jnohlgard
Copy link
Member

As I understand this PR, this is how a periph driver should configure its pins:

gpio_init(my_pin, GPIO_DIR_OUT, GPIO_NOPULL);
gpio_init_af(my_pin, GPIO_AF3);

I would like it to be so that periph drivers only need to make one call (gpio_init_af()), and that the GPIO registers are untouched when the AF is not GPIO_AF_GPIO. The GPIO registers are only needed to configure the pin when using it as GPIO, if configuring for e.g. I2C you only need to touch the PORT registers.

I think there needs to be a mutex or lock of some kind when messing with the ctx variables, since it's a RMW cycle.

/**
* @brief Allocation of memory for each independent interrupt slot
*/
static isr_ctx_t isr_ctx[CTX_NUMOF] = {{0}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misleading initializer syntax, AFAIK this only specifies the value of the first element of the array, all other elements will be zero-filled, even if the {0} is changed to something else...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yapp, you are right, actually wrote a little test application earlier today to make sure... Thanks for this, @kaspar030! :-)

@haukepetersen
Copy link
Contributor Author

About the AF configuration for other peripherals:

You have a good point, this can made simpler for the Kinetis - how about 77c1635? The way to use this in other periph drivers is now very simple:

// simple usage
gpio_init_port(pin, GPIO_AF_3); 
or 
// af 6, open-drain with pull-up
gpio_init_port(pin,( GPIO_AF_6 | GPIO_PCR_OD | GPIO_PCR_PU)); 

The driver is now also prepared for simple adaption of #4862...

Regarding the thread safety: another very good point - which affects almost all out current GPIO implementations! -> I would tend to put this in a separate issue for now and find a global solution and apply that to all periph (GPIO) drivers once decided. Would that be ok with you?

@jnohlgard
Copy link
Member

@haukepetersen

Regarding the thread safety: another very good point - which affects almost all out current GPIO implementations! -> I would tend to put this in a separate issue for now and find a global solution and apply that to all periph (GPIO) drivers once decided. Would that be ok with you?

I agree with making it a separate issue. After reading through #4862 I have realized that all GPIO drivers are thread unsafe in varying degrees.

@jnohlgard
Copy link
Member

I like the reworked gpio_init_port()! It will make periph driver pin initialization dead simple.


for (int i = 0; i < 32; i++) {
if ((status & (1 << i)) && (port->PCR[i] & PORT_PCR_IRQC_MASK)) {
port->PCR[i] |= PORT_PCR_ISF_MASK;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the ISFR register instead to avoid having to do a RMW cycle:

port->ISFR = (1 << i);

@jnohlgard
Copy link
Member

I have a feeling there is a possible race condition between enabled pin IRQs and changing the configuration to disable interrupts (even with the safeguard inside gpio_init_af), but I guess setting pins as interrupt sources and then reconfiguring them to another mode while interrupts are still coming in is mostly an academic case with no danger to real world applications and can be deferred to the GPIO thread safety issue.

@haukepetersen
Copy link
Contributor Author

I have a feeling there is a possible race condition between enabled pin IRQs and changing the configuration to disable interrupts

I think this should be fine, l211 to l220 should make sure, that the context for an interrupt channel does actually exist until the channel is disabled.

But the race conditions while initializing pins are pretty scary, though not much (or no) code until now seemed to have been effected by this?! Anyway I opened an issue for this: #4866

@haukepetersen
Copy link
Contributor Author

using ISFR reg to clear interrupt flag in ISR now

if ((status & (1 << i)) && (port->PCR[i] & PORT_PCR_IRQC_MASK)) {
port->ISFR = (1 << i);
int ctx = get_ctx(port_num, i);
isr_ctx[ctx].cb(isr_ctx[ctx].arg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a safeguard, add a check/assert for cb != NULL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided actively against this to keep the ISR routine as short as possible. As the ctx is only modified in this driver, the chance of it accidentally being set to NULL is zero so I think it is save here to go without the check. The ISR is slow enough already...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, agreed.

@jnohlgard
Copy link
Member

@haukepetersen do you have access to the pba-d-01-kw2x and frdm-k64f boards for testing?

@haukepetersen
Copy link
Contributor Author

Yes I do, will test again this afternoon

@haukepetersen
Copy link
Contributor Author

Sorry, just noticed that my pba boards are all in Nuremberg at the Embedded world, so testing them will have to wait until Friday.

@cgundogan, @PeterKietzmann: Or might you be so nice and give this PR a test on the pba board?

@haukepetersen
Copy link
Contributor Author

tested successfully for the frdm-k64f

@haukepetersen
Copy link
Contributor Author

also tested successfully for the pba-d-01-kw2x

@jnohlgard jnohlgard added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Feb 27, 2016
@jnohlgard
Copy link
Member

OK to squash

@OlegHahm OlegHahm added the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label Feb 27, 2016
@jnohlgard
Copy link
Member

ACK, please squash

@haukepetersen
Copy link
Contributor Author

rebased and squashed

@haukepetersen haukepetersen removed the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label Feb 29, 2016
OlegHahm added a commit that referenced this pull request Feb 29, 2016
cpu/kinetis: reworked GPIO driver implementation
@OlegHahm OlegHahm merged commit c8403e4 into RIOT-OS:master Feb 29, 2016
@haukepetersen
Copy link
Contributor Author

Awesome! @gebart: thanks for your help and patience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants