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

How do I control GPIO? #944

Closed
marduino opened this Issue Jul 27, 2016 · 37 comments

Comments

Projects
None yet
7 participants
@marduino

marduino commented Jul 27, 2016

Dear sir,

I am using hikey runing optee, I wish to write a very simple application based on hello_world that can response a command to set and clear GPIO, I am not sure whether I can use on board LED, if OK that will be perfect!

I dont know how to do it, where should I invoke gpio init and set, which function should I use? Is there any guidance?

@d3zd3z

This comment has been minimized.

Contributor

d3zd3z commented Jul 27, 2016

There are some gpio patches in process, and there may be some examples. You may have to add a driver to optee_os, though, rather than just being able to do this in the TA.

@vchong

This comment has been minimized.

Contributor

vchong commented Jul 27, 2016

@marduino Let's wait for the patch above to get merged first so you don't have to change your code later because the interface is a bit different. Once merged I'll try to provide some guide.

@marduino

This comment has been minimized.

marduino commented Jul 28, 2016

Hi @d3zd3z and @vchong,

Thanks for the information! I have just read these patches, looks they are invoked inside of optee core, my question is how do I access gpio in my TA application, either directly or indirectly? I dont see API towards TA.

I just want know how to access hardware in TA, if SPI also OK for me, I havenot see SPI APIs which can be used by TA.

@vchong

This comment has been minimized.

Contributor

vchong commented Jul 28, 2016

@marduino GP doesn't provide an interface for bus protocols (GPIO, SPI, I2C, etc), so you'll have to write a static TA to access these functions. See https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/sta/sta_self_tests.c as reference.

@marduino

This comment has been minimized.

marduino commented Jul 29, 2016

@vchong,

If dont provide specific interface for bus protocols, can we use other mechanism to let dynamic loaded TA communicate or invoke bus protocals related APIs? Thanks.

@vchong

This comment has been minimized.

Contributor

vchong commented Jul 29, 2016

@marduino If you must use a dynamic TA, can you use it like a 'middle man' between your host program (client app) and the static TA? The dynamic TA can communicate with the static TA using Internal Client APIs. You'll still need a static TA, but I don't know of a better way.

@d3zd3z

This comment has been minimized.

Contributor

d3zd3z commented Jul 29, 2016

I suppose it would be possible to extend the TA API (libutee), since there are mechanisms for extending it. But, these would take some significant consideration because it becomes a much more long-lived API (and indeed ABI) and needs to be carefully thought out.

@marduino

This comment has been minimized.

marduino commented Aug 1, 2016

hi @vchong @d3zd3z,

Sorry for reply late, I prefer David's advise. Adding APIs is efficient, even it is obviously working for adding static TA to route, while it introduces complexity and low efficiency.

For adding long-lived API support, if there is any real existing risk(even I donot see for now), I think we'd better to consider and collect.

@marduino marduino closed this Aug 1, 2016

@d3zd3z

This comment has been minimized.

Contributor

d3zd3z commented Aug 1, 2016

My advice wasn't as much to suggest adding an API, but to explain the complexities doing that. It needs to be done in a way it doesn't break anything that GP specifies, and is forward-looking.

@marduino

This comment has been minimized.

marduino commented Aug 2, 2016

@d3zd3z,

I think adding APIs is less complex than any other solutions, mean while, GP doesnt say we can't add bus APIs.

GP needs provide standards on this kind of APIs, as it is impossible to avoid peripheral access. But before GP has this standard interface, while GP doesnt say we cannt do it, this is the best way I can imagine at this time.

@toddkuhreng

This comment has been minimized.

toddkuhreng commented Aug 5, 2016

Hi @vchong @marduino ,

I just went through the pl061_gpio.c driver, but how do I use this to access a particular GPIO pin from a static TA? Where and how do I initialize this GPIO driver?

So following are my questions:

  1. Where do I call pl061_init(struct pl061_data *pd) and pl061_register(vaddr_t base_addr, unsigned int gpio_dev) ? It shouldn't be from the static TA, right? It has to be part of hikey platform initialization, right?
  2. If I have to call the pl061_init and pl061_register, can you please tell me the parameter values I have to pass to these functions? what value shall I give for base_addr, gpio_dev and pl061_data?
  3. After intialization of GPIO controller, I can call pl061_set_direction and pl061_set_value from static TA, right? Is there anything else I have to do to get this working?

Is there any reference code to see how to initialize this GPIO controller and access GPIO pin?
Could some help me here? Thanks a lot.

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 7, 2016

@toddkuhreng

  1. I guess there's nothing really to prevent you from calling them from a static TA, but it does seem to make more sense for them to be part of platform initialization.
  2. HiKey Reference Code
    For HiKey, there are 20 groups of GPIO pins, with below defined base addresses. You would put these in a header file somewhere. Each group has 8 GPIOs, for a total of 160. So, in the platform's conf.mk file, you'll need to pass in arm64-platform-cflags += -DPLAT_PL061_MAX_GPIOS=160 or arm32-platform-cflags += -DPLAT_PL061_MAX_GPIOS=160. If using another platform, you'll need to check the spec for it for the proper base addresses and max number of GPIOs.
#define GPIO0_BASE              0xF8011000 //gpio pin numbers 0-7
#define GPIO1_BASE              0xF8012000 //8-15
#define GPIO2_BASE              0xF8013000 //16-23
#define GPIO3_BASE              0xF8014000 //24-31
#define GPIO4_BASE              0xF7020000 //32-39
#define GPIO5_BASE              0xF7021000 //40-47
#define GPIO6_BASE              0xF7022000 //48-55
#define GPIO7_BASE              0xF7023000 //56-63
#define GPIO8_BASE              0xF7024000 //64-71
#define GPIO9_BASE              0xF7025000 //72-79
#define GPIO10_BASE             0xF7026000 //80-87
#define GPIO11_BASE             0xF7027000 //88-95
#define GPIO12_BASE             0xF7028000 //96-103
#define GPIO13_BASE             0xF7029000 //104-111
#define GPIO14_BASE             0xF702A000 //112-119
#define GPIO15_BASE             0xF702B000 //120-127
#define GPIO16_BASE             0xF702C000 //128-135
#define GPIO17_BASE             0xF702D000 //136-143
#define GPIO18_BASE             0xF702E000 //144-151
#define GPIO19_BASE             0xF702F000 //152-159

main.c (or somewhere else appropriate)

register_phys_mem(MEM_AREA_IO_NSEC, GPIO6_BASE, 0x1000);
//repeat for each base you want to use, e.g.
//register_phys_mem(MEM_AREA_IO_NSEC, GPIO0_BASE, 0x1000);
//register_phys_mem(MEM_AREA_IO_NSEC, GPIO19_BASE, 0x1000);

static struct pl061_data platform_pl061_data;

//repeat for each base you want to use, or create a general version of this function that caters to all bases
static vaddr_t gpio6_base(void)
{
    static void *va4;

    if (cpu_mmu_enabled()) {
        if (!va4)
            va4 = phys_to_virt(GPIO6_BASE, MEM_AREA_IO_NSEC);
        return (vaddr_t)va4;
    }
    return GPIO6_BASE;
}

static TEE_RESULT init_gpio(void)
{
    pl061_init(&platform_pl061_data);
    pl061_register(gpio6_base(), 6);
    //repeat for each base you want to use, e.g.
    //pl061_register(gpio0_base(), 0);
    //pl061_register(gpio19_base(), 19);
    return TEE_SUCCESS;
}

driver_init(init_gpio);

platform_pl061_data.chip is an instance of the gpio driver. In main.c above, you can use it directly. E.g.

    platform_pl061_data.chip.ops->set_value(some_gpio_pin_number, GPIO_LEVEL_LOW);
    platform_pl061_data.chip.ops->set_value(50, GPIO_LEVEL_LOW);
    platform_pl061_data.chip.ops->set_value(another_gpio_pin_number, GPIO_LEVEL_HIGH);
    platform_pl061_data.chip.ops->set_value(52, GPIO_LEVEL_HIGH);
    platform_pl061_data.chip.ops->set_direction(50, GPIO_DIR_OUT);
    platform_pl061_data.chip.ops->set_direction(52, GPIO_DIR_IN);

If calling from a static TA, you'll need to somehow pass it the address of platform_pl061_data.chip, i.e. &platform_pl061_data.chip, before you can call the set/get dir/value functions. You can't call pl061_{set|get}_{direction|value} functions directly since they're static.

    struct gpio_chip    *gpio = &platform_pl061_data.chip;
    gpio->ops->set_value(50, GPIO_LEVEL_LOW);
    gpio->ops->set_value(52, GPIO_LEVEL_HIGH);
    gpio->ops->set_direction(50, GPIO_DIR_OUT);
    gpio->ops->set_direction(52, GPIO_DIR_IN);
@vchong

This comment has been minimized.

Contributor

vchong commented Aug 7, 2016

Alternatively, instead of passing the address of platform_pl061_data.chip to the static TA, I suppose you can create wrapper functions in main.c or wherever for your platform_pl061_data.chip.ops->set* functions and just call these functions directly. E.g.

main.c

void do_some_gpio_calls(void)
{
    platform_pl061_data.chip.ops->set_value(52, GPIO_LEVEL_HIGH);
    platform_pl061_data.chip.ops->set_direction(50, GPIO_DIR_OUT);
}

static TA

void foo(void)
{
    do_some_gpio_calls();
}
@vchong

This comment has been minimized.

Contributor

vchong commented Aug 7, 2016

Also note that if for any reason the normal world is also manipulating the same gpio at the same time, or has muxed it to another function, you might get interference or undefined behavior. This is a different and bigger issue (sync/share access to devices from both worlds) which has yet to be resolved. https://lkml.org/lkml/2015/10/29/287 and #679 are two pieces of that puzzle.

@toddkuhreng

This comment has been minimized.

toddkuhreng commented Aug 7, 2016

@vchong ,

Thank you so much for explaining this very well. So I basically added a gpio.c to the directory optee_os/core/arch/arm/play-hikey. However, I couldn't test this because these GPIO number are confusing for me. Could you please help me with this too?

I would like to set direction and value of GPIO_A which is basically 23rd pin in low speed expansion connector.

gpio_pins_hikey

So I am running Android in Hikey board and following is how I accessed this GPIO_A through adb shell. (Digging up the 96boards blogs, I found the GPIO number for GPIO_A (23rd pin) for hikey running Android is 488)

# adb shell
# cd sys/class/gpio/
# echo 488 > export 
# cd gpio488
# echo out > direction
# echo 1 > value

This worked. Now, I am trying to do this from my static TA (from secure world).

To summarise, I have two questions:

  1. Basically, I would like to set direction and value of this 23rd pin (GPIO_A) of Hikey board which is running Android from my static TA. So to which group this GPIO_A pin belongs too ? what pin number should I use for GPIO_A in the function platform_pl061_data.chip.ops->set_value(488, GPIO_LEVEL_HIGH)? Is it 488? Please let me know.
  2. Looking at the diagram above I thought there are only 12 GPIO pins (GPIO_A to GPIO_L) for Hikey board. However, the documentation Hikey says, "there are 160 GPIO pins". Could you please help me to understand this part too?

Thank you so much,

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 8, 2016

Yes, the GPIO numbering by itself can be confusing. The secure world (rather the driver to be specific) uses its own numbering, which adds another layer of complexity perhaps, but it is separate from that of the normal world / linux, so try to just not get both mixed up together. GPIO_A or 488 has no meaning in this case.

  1. You need to look at the schematic. The 23rd pin on the LS connector is gpio2_0, which gives you pin number 16 based on the reference code above.
  2. 12 is just what's drawn out to the LS connector for you to conveniently use as GPIO. There are a total of 160, but not all are or can be drawn out to the LS connector, which is only 40 pins anyway. Also, not all GPIO pins are used as GPIO. As mentioned earlier, they can be muxed to a different function. E.g. all the I2C and SPI pins can be configured as GPIO, or rather these GPIO pins have been configured as I2C or SPI, as GPIO is usually the default. See https://lwn.net/Articles/468759/ if you really want to get into it.
@toddkuhreng

This comment has been minimized.

toddkuhreng commented Aug 18, 2016

Hi @vchong ,

Yes, I just tested this and it worked. Thank you so much @vchong .

I have one another question. Is it possible to make sure that normal world cannot access this gpio2_0 (23rd pin of LS connector). That is, only secure world can read and write to this pin (on Hikey)?

Thank you so much,

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 18, 2016

@toddkuhreng Good to know. :) You're welcome.

As for making securing access to the pin, see previous comment #944 (comment) above. There's still some (perhaps a lot of) work to do before this becomes possible.

@toddkuhreng

This comment has been minimized.

toddkuhreng commented Sep 27, 2016

Hi @vchong ,

I was wondering what will following function do if I pass MEM_AREA_IO_SEC instead of MEM_AREA_IO_NSEC? Will that make this GPIO port only accessible to secure world?

register_phys_mem(MEM_AREA_IO_SEC, GPIO6_BASE, 0x1000);

Thank you so much,

@sorenb-xlnx

This comment has been minimized.

Contributor

sorenb-xlnx commented Sep 27, 2016

That will just set the corresponding bits in the translation table and cause the protection bits on the AXI bus to be set accordingly. Enforcing the protection still needs to be done by something in the interconnect or the slave device. As long as nothing enforces protection you can set whatever you want in the translation table and it will work.

@vchong

This comment has been minimized.

Contributor

vchong commented Sep 27, 2016

@sorenb-xlnx Thanks! : )

@paullgnc

This comment has been minimized.

paullgnc commented Apr 4, 2017

Hi @toddkuhreng,
I am currently trying to do the exact same thing you did : access gpio I/O from TA.
I used to do it using Linux command exactly how you did (but on gpio489) and I am about to proceed how explain here.

However, I am using an UART Adapter Board (http://www.96boards.org/product/uarts/) for the board <-> computer communication. As soon as the pin 23 is connected to this adapter, I was wondering if you were using it, and either this pin is used or not by something else on the board, i.e. "can I use it safely ?"

Moreover, I am kind of lost in all these gpio/pin numbers. If I want to use GPIO_C (pin 25) instead, I will have to change GPIO2_BASE for GPIO3_BASE and just specify 25 instead of 23 when using set_value and set_direction ?

Finally, by default, PLAT_PL601_MAX_GPIOS is defined to 32, if I do not pass arm64-platform-cflags += -DPLAT_PL061_MAX_GPIOS=160 as mentioned above, what will it do ?

@vchong

This comment has been minimized.

Contributor

vchong commented Apr 5, 2017

@paullgnc GPIO_C (pin 25) is pin number 18 here in optee_os, which is what you need to specify when using set_value and set_direction. The base is still the same, i.e. GPIO2_BASE.

For PLAT_PL061_MAX_GPIOS (NOT PLAT_PL601_MAX_GPIOS), 32 is just a random default. For hikey, we pass in -DPLAT_PL061_MAX_GPIOS=160 because that's the actual number of GPIO pins available for that platform. If you don't pass that, you'll only have access or be able to use the first 32 pins. You should pass in a number that matches the platform you're using, and only if it uses PL061 for GPIO.

@paullgnc

This comment has been minimized.

paullgnc commented Apr 5, 2017

@vchong, thank you for your help. I did not know pin number 25 is pin number 18.
In fact, I found out there are a few lines in optee_os/core/arch/arm/plat-hikey.conf.mk that already define PLAT_PL061_MAX_GPIOS.

CFG_PL061 ?= y
...
ifeq ($(CFG_PL061),y)
core-platform-cppflags		+= -DPLAT_PL061_MAX_GPIOS=160
endif

So, PLAT_PL061_MAX_GPIOS is defined to 160 for core-platform-cppflags and I should add it to arm32-platform-cflags as well, right ?
Sorry if it seems to be a dumb question but I am kind of lost in all those parameters.
Finally, it looks like these are parameters for 32 bits, but I build my TA using 64 bits cross compiler. Should I build it with 32 bits cross compiler ?

@vchong

This comment has been minimized.

Contributor

vchong commented Apr 5, 2017

You're welcome. Yes, if you're using hikey, PLAT_PL061_MAX_GPIOS is already defined for you in both 64- and 32-bit builds so you don't have to worry about it. Then all you basically need is something like:

#define PIN18              18
#define GPIO2_BASE              0xF8013000 //16-23
register_phys_mem(MEM_AREA_IO_NSEC, GPIO2_BASE, 0x1000);

void some_function(void)
{
    struct pl061_data pd;
    pl061_init(&pd);
    pl061_register(nsec_periph_base(GPIO2_BASE), 2);
    pl061_set_mode_control(PIN18, PL061_MC_SW);
    pd.chip.ops->set_interrupt(PIN18, GPIO_INTERRUPT_DISABLE);
    pd.chip.ops->set_direction(PIN18, GPIO_DIR_OUT); //or GPIO_DIR_IN
    pd.chip.ops->set_value(PIN18, GPIO_LEVEL_HIGH); //or GPIO_LEVEL_LOW
}
@paullgnc

This comment has been minimized.

paullgnc commented Apr 10, 2017

Hi @vchong,
The last but not least question, what do I have to add as header file in my ta to allow to use these functions some_function(void)?
I just tried to call it in the middle of the code because I thought they would be loaded automatically. However, I am facing issues like 'implicit declaration' and I have no idea how to fix it. I understand I should add some #include, but I do not know which one.

@vchong

This comment has been minimized.

Contributor

vchong commented Apr 10, 2017

TAs (~userland) have no direct access to the GPIO APIs (~kernel). See #1461 for a similar issue (except that he's using the SPI driver and you're using the GPIO one) and recommendations for 'fixes'.

@paullgnc

This comment has been minimized.

paullgnc commented Apr 10, 2017

As long as I understand, I can do use GPIO APIs in kernel but not in userspace. So, if I make a user TA it is not possible, but it can be made in a static TA as @toddkuhreng did ?
My question is, if I change my user TA into a static TA, should it work or not ? Is it hard to do move from a user TA to a static TA ?

@vchong

This comment has been minimized.

Contributor

vchong commented Apr 12, 2017

Yes, it can be made in a static TA (now known as a pseudo TA) and should work. It should not be hard to move from user TA to pseudo TA as they're very similar. As mentioned in #1461, see https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/pta/interrupt_tests.c or other files in the same folder for examples of pseudo TAs.

@mKadar

This comment has been minimized.

mKadar commented Aug 9, 2017

Hi all,

How can I catch GPIO interrupts in OPTEE OS? I am trying to write a controller with a button and buzzer. When the button is pressed, the buzzer should ring.

Is it possible to implement a TA which does not return?
At first, I wanted to start/stop the controller in a dynamic TA via a request from a client (Normal World). The controller would have been an infinite loop (reading button and sending command to buzzer). But it is not possible as TA are single threading and the client waits the TA to return (InvokeCommand).

I have the same problem with a request from a dynamic TA to a PTA. That is why I was thinking about using GPIO interrupts on the HiKey. A handler should catch interrupt and realise the monitoring. In OPTEE OS, there are functions to enable/disable interrupts, but I didn't find any handler.

Are there any equivalents to request_irq function in Linux for OPTEE OS?

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 10, 2017

@mKadar Regarding gpio interrupts, the gpio (pl061) driver is a relatively simple one ported over from arm-tf and as such there's no equivalent to that in Linux atm. Patches are welcome.

For the TA, I'm not sure but maybe you can try researching TA_FLAG_INSTANCE_KEEP_ALIVE. See #1590 for reference.

@mKadar

This comment has been minimized.

mKadar commented Aug 16, 2017

Thank you @vchong.

I think TA_FLAGS_INSTANCE_KEEP_ALIVE flag does not solve my problem. Ideally I would like to run a sevice, an infinite loop in the TA to realise treatments. As there is no multi-threading in dynamic TA, I think it would never return to the monitor. My guess is that monitor gets control once TEE_InvokeCommandEntryPoint has returned. Is this correct?

That is why I think the best solution is to realise the treatments in optee OS, using a thread or interrupts. As gpio interrupts are not supported, I am considering using a secure timer. This issue #1541 gives a good starting point. However I have the impression that GIC controller is not used by HiKey platform. In core/arch/arm/plat-hikey/main.c the function main_fiq is not implemented.
static void main_fiq(void){ panic() }
I imagine main.c should initialise GIC driver and main_fiq should handle its interrupts, such as for STM platforms:
static void main_fiq(void) { gic_it_handle(&gic_data); }
Am I right?

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 16, 2017

Is this correct?

yes

Am I right?

yes, i think gic is not used by hikey atm

Sorry but not sure how to help you really. Maybe you can try looking at https://github.com/jenswi-linaro/optee_os/commits/tui-old2 and jenswi-linaro@eeff384. It has some code that handles mouse clicks via interrupts. Note that this is an old PoC that's currently not supported on the main/master branch.

#1724 (comment) and #1724 (comment) also have some good info regarding memory and threads which might be useful references.

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 16, 2017

Just realized that #1541 already referenced ps2mouse.c and interrupt_tests.c, so you don't really need to look at https://github.com/jenswi-linaro/optee_os/commits/tui-old2.

Also, the concept of Early TAs (#1733) might be useful maybe.

@mKadar

This comment has been minimized.

mKadar commented Aug 17, 2017

Thank you @vchong for your quick reply. I will try to get inspired by ps2mouse.c. Do you think modifying ARM Trusted Firmware is required? Or does it already handle FIQ redirection? In #1728 @SimonWan has updated secure timer register and I don't understand what these registers are and what they are for.

Another question I have is, how can I allocate GPIO ports to the trusted world only? For now, I can access GPIO from both worlds. I guess it relies on ARM Trusted Firmware code. I didn't find direct information on how it is implemented in ARM Trusted Firmware. Is it a simple configuration change or does it need to add/modify code?

@vchong

This comment has been minimized.

Contributor

vchong commented Aug 17, 2017

I'm not too familiar with those registers either, but Simon only got it to work with TSP, not optee_os. For simplicity's sake, think of TSP as a very very simple version of optee_os written by ARM before optee_os came along.

I don't think there's a mechanism mature enough right now (at least not in the open) to allocate any io (not just gpio) to secure world only. Hardware-wise, a device will also need something like a TZPC (http://infocenter.arm.com/help/topic/com.arm.doc.dto0015a/DTO0015_primecell_infrastructure_amba3_tzpc_bp147_to.pdf) to prevent non-secure access to peripherals.

@mKadar

This comment has been minimized.

mKadar commented Aug 17, 2017

It is now clearer to me. Thank you very much for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment