From ef4a4ea6541aedc9987f5722ccccac43fe7f3e1a Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 7 May 2013 12:10:47 +0200 Subject: [PATCH] Implement input driver for i.MX53 tablet (fix #724) The input driver implements support for the Egalaxy touchscreen and for Freescale's MPR121 capacitative touch buttons. --- .../platform/imx53/drivers/board_base.h | 8 + .../imx53/platform_session/platform_session.h | 7 +- os/run/demo.run | 7 +- os/src/drivers/input/imx53/driver.h | 106 +++++++++++ os/src/drivers/input/imx53/egalax_ts.h | 93 +++++++++ os/src/drivers/input/imx53/i2c.h | 178 ++++++++++++++++++ os/src/drivers/input/imx53/main.cc | 78 ++++++++ os/src/drivers/input/imx53/mpr121.h | 118 ++++++++++++ os/src/drivers/input/imx53/target.mk | 5 + os/src/drivers/platform/imx53/ccm.h | 19 +- os/src/drivers/platform/imx53/iomux.h | 49 ++++- os/src/drivers/platform/imx53/main.cc | 13 +- 12 files changed, 672 insertions(+), 9 deletions(-) create mode 100644 os/src/drivers/input/imx53/driver.h create mode 100644 os/src/drivers/input/imx53/egalax_ts.h create mode 100644 os/src/drivers/input/imx53/i2c.h create mode 100644 os/src/drivers/input/imx53/main.cc create mode 100644 os/src/drivers/input/imx53/mpr121.h create mode 100644 os/src/drivers/input/imx53/target.mk diff --git a/base/include/platform/imx53/drivers/board_base.h b/base/include/platform/imx53/drivers/board_base.h index 3a423957f..544a1d7c0 100644 --- a/base/include/platform/imx53/drivers/board_base.h +++ b/base/include/platform/imx53/drivers/board_base.h @@ -92,6 +92,14 @@ namespace Genode CCM_BASE = 0x53FD4000, CCM_SIZE = 0x00004000, + I2C_2_IRQ = 63, + I2C_2_BASE = 0x63fc4000, + I2C_2_SIZE = 0x00004000, + + I2C_3_IRQ = 64, + I2C_3_BASE = 0x53fec000, + I2C_3_SIZE = 0x00004000, + IIM_BASE = 0x63f98000, IIM_SIZE = 0x00004000, diff --git a/os/include/platform/imx53/platform_session/platform_session.h b/os/include/platform/imx53/platform_session/platform_session.h index e30e8e634..5e09cd109 100644 --- a/os/include/platform/imx53/platform_session/platform_session.h +++ b/os/include/platform/imx53/platform_session/platform_session.h @@ -21,7 +21,12 @@ namespace Platform { struct Session : Genode::Session { - enum Device { IPU }; + enum Device { + IPU, + I2C_2, + I2C_3, + BUTTONS, + }; enum Board_revision { SMD = 2, /* Freescale i.MX53 SMD Tablet */ diff --git a/os/run/demo.run b/os/run/demo.run index 88811ced4..fa0ff914a 100644 --- a/os/run/demo.run +++ b/os/run/demo.run @@ -75,7 +75,11 @@ append_if [have_spec imx53] config { -} + + + + + } append_if [have_spec ps2] config { @@ -126,6 +130,7 @@ lappend_if [have_spec framebuffer] boot_modules fb_drv lappend_if [have_spec usb] boot_modules usb_drv lappend_if [have_spec gpio] boot_modules gpio_drv lappend_if [have_spec imx53] boot_modules platform_drv +lappend_if [have_spec imx53] boot_modules input_drv build_boot_image $boot_modules diff --git a/os/src/drivers/input/imx53/driver.h b/os/src/drivers/input/imx53/driver.h new file mode 100644 index 000000000..2ee7c057b --- /dev/null +++ b/os/src/drivers/input/imx53/driver.h @@ -0,0 +1,106 @@ +/* + * \brief Input-driver + * \author Stefan Kalkowski + * \date 2013-03-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _DRIVER_H_ +#define _DRIVER_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include +#include + +namespace Input { + class Tablet_driver; +} + + +class Input::Tablet_driver : Genode::Thread<8192> +{ + private: + + enum Gpio_irqs { + GPIO_TOUCH = 84, + GPIO_BUTTON = 132, + }; + + Event_queue &_ev_queue; + Gpio::Connection _gpio_ts; + Gpio::Connection _gpio_bt; + Genode::Signal_receiver _receiver; + Genode::Signal_context _ts_rx; + Genode::Signal_context _bt_rx; + Genode::Signal_context_capability _ts_sig_cap; + Genode::Signal_context_capability _bt_sig_cap; + Touchscreen _touchscreen; + Buttons _buttons; + + Genode::Signal_context_capability _init_ts_gpio() + { + Genode::Signal_context_capability ret = _receiver.manage(&_ts_rx); + _gpio_ts.direction(Gpio::Session::OUT); + _gpio_ts.write(true); + _gpio_ts.direction(Gpio::Session::IN); + _gpio_ts.irq_sigh(_ts_sig_cap); + _gpio_ts.irq_type(Gpio::Session::LOW_LEVEL); + _gpio_ts.irq_enable(true); + return ret; + } + + Genode::Signal_context_capability _init_bt_gpio() + { + Genode::Signal_context_capability ret = _receiver.manage(&_bt_rx); + _gpio_bt.direction(Gpio::Session::OUT); + _gpio_bt.write(true); + _gpio_bt.direction(Gpio::Session::IN); + _gpio_bt.irq_sigh(_bt_sig_cap); + _gpio_bt.irq_type(Gpio::Session::FALLING_EDGE); + _gpio_bt.irq_enable(true); + return ret; + } + + Tablet_driver(Event_queue &ev_queue) + : _ev_queue(ev_queue), + _gpio_ts(GPIO_TOUCH), + _gpio_bt(GPIO_BUTTON), + _ts_sig_cap(_init_ts_gpio()), + _bt_sig_cap(_init_bt_gpio()) { start(); } + + public: + + static Tablet_driver* factory(Event_queue &ev_queue); + + void entry() + { + while (true) { + Genode::Signal sig = _receiver.wait_for_signal(); + if (sig.context() == &_ts_rx) + _touchscreen.event(_ev_queue); + else if (sig.context() == &_bt_rx) + _buttons.event(_ev_queue); + } + } +}; + + +Input::Tablet_driver* Input::Tablet_driver::factory(Event_queue &ev_queue) +{ + static Input::Tablet_driver driver(ev_queue); + return &driver; +} + +#endif /* _DRIVER_H_ */ diff --git a/os/src/drivers/input/imx53/egalax_ts.h b/os/src/drivers/input/imx53/egalax_ts.h new file mode 100644 index 000000000..8baf9b176 --- /dev/null +++ b/os/src/drivers/input/imx53/egalax_ts.h @@ -0,0 +1,93 @@ +/* + * \brief EETI eGalaxy touchscreen driver + * \author Stefan Kalkowski + * \date 2013-03-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _EGALAX_TS_H_ +#define _EGALAX_TS_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include + +namespace Input { + class Touchscreen; +} + + +class Input::Touchscreen { + + private: + + enum I2c_addresses { I2C_ADDR = 0x4 }; + enum Finger_state { PRESSED, RELEASED }; + + Genode::Attached_io_mem_dataspace _i2c_ds; + I2c::I2c _i2c; + Genode::uint8_t _buf[10]; + Finger_state _state; + + public: + + Touchscreen() : _i2c_ds(Genode::Board_base::I2C_3_BASE, + Genode::Board_base::I2C_3_SIZE), + _i2c((Genode::addr_t)_i2c_ds.local_addr(), + Genode::Board_base::I2C_3_IRQ), + _state(RELEASED) + { + /* ask for touchscreen firmware version */ + Genode::uint8_t cmd[10] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; + _i2c.send(I2C_ADDR, cmd, sizeof(cmd)); + } + + void event(Event_queue &ev_queue) + { + _i2c.recv(I2C_ADDR, _buf, sizeof(_buf)); + + /* ignore all events except of multitouch*/ + if (_buf[0] != 4) + return; + + int x = (_buf[3] << 8) | _buf[2]; + int y = (_buf[5] << 8) | _buf[4]; + + Genode::uint8_t state = _buf[1]; + bool valid = state & (1 << 7); + int id = (state >> 2) & 0xf; + int down = state & 1; + + if (!valid || id > 5) + return; /* invalid point */ + + x = 102400 / (3276700 / x); + y = 76800 / (3276700 / y); + + /* motion event */ + ev_queue.add(Input::Event(Input::Event::MOTION, 0, x, y, 0, 0)); + + /* button event */ + if ((down && (_state == RELEASED)) || + (!down && (_state == PRESSED))) { + ev_queue.add(Input::Event(down ? Input::Event::PRESS + : Input::Event::RELEASE, + Input::BTN_LEFT, 0, 0, 0, 0)); + _state = down ? PRESSED : RELEASED; + } + } +}; + +#endif /* _EGALAX_TS_H_ */ diff --git a/os/src/drivers/input/imx53/i2c.h b/os/src/drivers/input/imx53/i2c.h new file mode 100644 index 000000000..637abbe30 --- /dev/null +++ b/os/src/drivers/input/imx53/i2c.h @@ -0,0 +1,178 @@ +/* + * \brief Driver for the i.MX53 i2c controller + * \author Stefan Kalkowski + * \date 2013-03-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _I2C_H_ +#define _I2C_H_ + +/* Genode includes */ +#include +#include +#include + +namespace I2c +{ + class I2c; +} + + +class I2c::I2c : Genode::Mmio +{ + private: + + struct Address : public Register<0x0, 8> + { + struct Addr : Bitfield<1,7> {}; + }; + + struct Freq_divider : public Register<0x4, 8> {}; + + struct Control : public Register<0x8, 8> + { + struct Repeat_start : Bitfield<2,1> {}; + struct Tx_ack_enable : Bitfield<3,1> {}; + struct Tx_rx_select : Bitfield<4,1> {}; + struct Master_slave_select : Bitfield<5,1> {}; + struct Irq_enable : Bitfield<6,1> {}; + struct Enable : Bitfield<7,1> {}; + }; + + struct Status : public Register<0xc, 8> + { + struct Rcv_ack : Bitfield<0,1> {}; + struct Irq : Bitfield<1,1> {}; + struct Slave_rw : Bitfield<2,1> {}; + struct Arbitration_lost : Bitfield<4,1> {}; + struct Busy : Bitfield<5,1> {}; + struct Addressed_as_slave : Bitfield<6,1> {}; + struct Data_transfer : Bitfield<7,1> {}; + }; + + struct Data : public Register<0x10, 8> { }; + + + class No_ack : Genode::Exception {}; + class No_irq : Genode::Exception {}; + + + Timer::Connection _timer; + Genode::Irq_connection _irq; + + void _busy() { while (!read()); } + + void _start() + { + /* clock enable */ + + write(0x2c); + write(0); + write(Control::Enable::bits(1)); + + _timer.msleep(1); + + write(1); + + _busy(); + + write(Control::Tx_rx_select::bits(1) | + Control::Tx_ack_enable::bits(1) | + Control::Irq_enable::bits(1) | + Control::Master_slave_select::bits(1) | + Control::Enable::bits(1)); + } + + void _stop() + { + write(0); + + /* clock disable */ + } + + void _write(Genode::uint8_t value) + { + write(value); + + _irq.wait_for_irq(); + if (!read()) throw No_irq(); + write(0); + if (read()) throw No_ack(); + } + + public: + + I2c(Genode::addr_t const base, unsigned irq) : Mmio(base), _irq(irq) + { + write(0); + write(0); + } + + void send(Genode::uint8_t addr, const Genode::uint8_t *buf, + Genode::size_t num) + { + while (true) { + try { + _start(); + + _write(addr << 1); + for (Genode::size_t i = 0; i < num; i++) + _write(buf[i]); + + _stop(); + return; + } catch(No_ack) { + } catch(No_irq) { + } + _stop(); + } + } + + + void recv(Genode::uint8_t addr, Genode::uint8_t *buf, + Genode::size_t num) + { + while (true) { + try { + _start(); + _write(addr << 1 | 1); + write(0); + if (num > 1) + write(0); + read(); /* dummy read */ + + for (Genode::size_t i = 0; i < num; i++) { + + _irq.wait_for_irq(); + if (!read()) throw No_irq(); + write(0); + + if (i == num-1) { + write(0); + write(0); + while (read()) ; + } else if (i == num-2) { + write(1); + } + + buf[i] = read(); + } + + _stop(); + return; + } catch(No_irq) { + } + _stop(); + } + } + +}; + +#endif /* _I2C_H_ */ diff --git a/os/src/drivers/input/imx53/main.cc b/os/src/drivers/input/imx53/main.cc new file mode 100644 index 000000000..861fffae2 --- /dev/null +++ b/os/src/drivers/input/imx53/main.cc @@ -0,0 +1,78 @@ +/** + * \brief Input driver front-end + * \author Norman Feske + * \author Christian Helmuth + * \author Stefan Kalkowski + * \date 2006-08-30 + */ + +/* + * Copyright (C) 2006-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Genode; + +static Event_queue ev_queue; + +namespace Input { + + /* + * Event handling is disabled on queue creation and will be enabled later if a + * session is created. + */ + void event_handling(bool enable) + { + if (enable) + ev_queue.enable(); + else + ev_queue.disable(); + } + + bool event_pending() { return !ev_queue.empty(); } + Event get_event() { return ev_queue.get(); } +} + + +int main(int argc, char **argv) +{ + /* initialize server entry point */ + enum { STACK_SIZE = 4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "input_ep"); + + Platform::Connection plat_drv; + switch (plat_drv.revision()) { + case Platform::Session::SMD: + plat_drv.enable(Platform::Session::I2C_2); + plat_drv.enable(Platform::Session::I2C_3); + plat_drv.enable(Platform::Session::BUTTONS); + Input::Tablet_driver::factory(ev_queue); + default: + PWRN("No input driver available for this board"); + } + + /* entry point serving input root interface */ + static Input::Root input_root(&ep, env()->heap()); + + /* tell parent about the service */ + env()->parent()->announce(ep.manage(&input_root)); + + /* main's done - go to sleep */ + sleep_forever(); + return 0; +} diff --git a/os/src/drivers/input/imx53/mpr121.h b/os/src/drivers/input/imx53/mpr121.h new file mode 100644 index 000000000..4a1ba5c2f --- /dev/null +++ b/os/src/drivers/input/imx53/mpr121.h @@ -0,0 +1,118 @@ +/* + * \brief Freescale MPR121 capacitative button driver + * \author Stefan Kalkowski + * \date 2013-03-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _MPR121_H_ +#define _MPR121_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include + +namespace Input { + class Buttons; +} + + +class Input::Buttons { + + private: + + enum { + I2C_ADDR = 0x5a, + }; + + enum Events { + RELEASE = 0, + BACK = 1, + HOME = 2, + MENU = 4, + POWER = 8, + }; + + Genode::Attached_io_mem_dataspace _i2c_ds; + I2c::I2c _i2c; + int _button; + + public: + + Buttons() : _i2c_ds(Genode::Board_base::I2C_2_BASE, + Genode::Board_base::I2C_2_SIZE), + _i2c((Genode::addr_t)_i2c_ds.local_addr(), + Genode::Board_base::I2C_2_IRQ), + _button(0) + { + static Genode::uint8_t init_cmd[][2] = { + {0x41, 0x8 }, {0x42, 0x5 }, {0x43, 0x8 }, + {0x44, 0x5 }, {0x45, 0x8 }, {0x46, 0x5 }, + {0x47, 0x8 }, {0x48, 0x5 }, {0x49, 0x8 }, + {0x4a, 0x5 }, {0x4b, 0x8 }, {0x4c, 0x5 }, + {0x4d, 0x8 }, {0x4e, 0x5 }, {0x4f, 0x8 }, + {0x50, 0x5 }, {0x51, 0x8 }, {0x52, 0x5 }, + {0x53, 0x8 }, {0x54, 0x5 }, {0x55, 0x8 }, + {0x56, 0x5 }, {0x57, 0x8 }, {0x58, 0x5 }, + {0x59, 0x8 }, {0x5a, 0x5 }, {0x2b, 0x1 }, + {0x2c, 0x1 }, {0x2d, 0x0 }, {0x2e, 0x0 }, + {0x2f, 0x1 }, {0x30, 0x1 }, {0x31, 0xff}, + {0x32, 0x2 }, {0x5d, 0x4 }, {0x5c, 0xb }, + {0x7b, 0xb }, {0x7d, 0xc9}, {0x7e, 0x82}, + {0x7f, 0xb4}, {0x5e, 0x84}}; + + /* initialize mpr121 touch button device */ + for (unsigned i = 0; i < sizeof(init_cmd)/2; i++) + _i2c.send(I2C_ADDR, init_cmd[i], 2); + } + + void event(Event_queue &ev_queue) + { + Genode::uint8_t buf = 0; + + _i2c.send(I2C_ADDR, &buf, 1); + _i2c.recv(I2C_ADDR, &buf, 1); + switch (buf) { + case RELEASE: + ev_queue.add(Input::Event(Input::Event::RELEASE, + _button, 0, 0, 0, 0)); + break; + case BACK: + ev_queue.add(Input::Event(Input::Event::PRESS, + Input::KEY_BACK, 0, 0, 0, 0)); + _button = Input::KEY_BACK; + break; + case HOME: + ev_queue.add(Input::Event(Input::Event::PRESS, + Input::KEY_HOME, 0, 0, 0, 0)); + _button = Input::KEY_HOME; + break; + case MENU: + ev_queue.add(Input::Event(Input::Event::PRESS, + Input::KEY_MENU, 0, 0, 0, 0)); + _button = Input::KEY_MENU; + break; + case POWER: + ev_queue.add(Input::Event(Input::Event::PRESS, + Input::KEY_POWER, 0, 0, 0, 0)); + _button = Input::KEY_POWER; + break; + default: + /* just ignore everything else */; + }; + } +}; + +#endif /* _MPR121_H_ */ diff --git a/os/src/drivers/input/imx53/target.mk b/os/src/drivers/input/imx53/target.mk new file mode 100644 index 000000000..234b757d0 --- /dev/null +++ b/os/src/drivers/input/imx53/target.mk @@ -0,0 +1,5 @@ +TARGET = input_drv +REQUIRES = imx53 +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR) \ No newline at end of file diff --git a/os/src/drivers/platform/imx53/ccm.h b/os/src/drivers/platform/imx53/ccm.h index d8b6ee53f..fed2437b3 100644 --- a/os/src/drivers/platform/imx53/ccm.h +++ b/os/src/drivers/platform/imx53/ccm.h @@ -51,9 +51,16 @@ class Ccm : public Genode::Attached_io_mem_dataspace, struct Bypass_ipu_hs : Bitfield<18, 1> { }; }; + struct Ccgr1 : Register<0x6c, 32> + { + struct I2c_1 : Bitfield<18, 2> { }; + struct I2c_2 : Bitfield<20, 2> { }; + struct I2c_3 : Bitfield<22, 2> { }; + }; + struct Ccgr5 : Register<0x7c, 32> { - struct Ipu_clk_en : Bitfield<10, 2> { }; + struct Ipu : Bitfield<10, 2> { }; }; public: @@ -61,11 +68,15 @@ class Ccm : public Genode::Attached_io_mem_dataspace, Ccm() : Genode::Attached_io_mem_dataspace(Genode::Board_base::CCM_BASE, Genode::Board_base::CCM_SIZE), - Genode::Mmio((Genode::addr_t)local_addr()) {} + Genode::Mmio((Genode::addr_t)local_addr()) { } + + void i2c_1_enable(void) { write(3); } + void i2c_2_enable(void) { write(3); } + void i2c_3_enable(void) { write(3); } void ipu_clk_enable(void) { - write(3); + write(3); write(0); write(0); write(0xa2b32f0b); @@ -74,7 +85,7 @@ class Ccm : public Genode::Attached_io_mem_dataspace, void ipu_clk_disable(void) { - write(0); + write(0); write(1); write(1); } diff --git a/os/src/drivers/platform/imx53/iomux.h b/os/src/drivers/platform/imx53/iomux.h index 27e96b4fd..eaf9999ff 100644 --- a/os/src/drivers/platform/imx53/iomux.h +++ b/os/src/drivers/platform/imx53/iomux.h @@ -35,22 +35,67 @@ class Iomux : public Genode::Attached_io_mem_dataspace, struct Di1_vs_polarity : Bitfield<10,1> { }; }; + struct Key_col3 : Register<0x3c, 32> {}; + struct Key_row3 : Register<0x40, 32> {}; + + struct Eim_a24 : Register<0x15c, 32> { }; + + template + struct Sw_mux_ctl_pad_gpio : Register<0x314 + OFF*4, 32> { }; + + struct Sw_pad_ctl_pad_key_col3 : Register<0x364, 32> { }; + struct Sw_pad_ctl_pad_key_row3 : Register<0x368, 32> { }; + + struct Sw_pad_ctl_pad_eim_a24 : Register<0x4a8, 32> { }; + + template + struct Sw_pad_ctl_pad_gpio : Register<0x6a4 + OFF*4, 32> { }; + + struct I2c2_ipp_scl_in_select_input : Register<0x81c, 32> { }; + struct I2c2_ipp_sda_in_select_input : Register<0x820, 32> { }; + struct I2c3_ipp_scl_in_select_input : Register<0x824, 32> { }; + struct I2c3_ipp_sda_in_select_input : Register<0x828, 32> { }; + public: Iomux() : Genode::Attached_io_mem_dataspace(Genode::Board_base::IOMUXC_BASE, Genode::Board_base::IOMUXC_SIZE), - Genode::Mmio((Genode::addr_t)local_addr()) + Genode::Mmio((Genode::addr_t)local_addr()) { } + + void i2c_2_enable() { + write(0x14); + write(0); + write(0x12d); + write(0x14); + write(0); + write(0x12d); } - void enable_di1() + void i2c_3_enable() + { + write >(0x12); + write(0x1); + write >(0x12d); + write >(0x12); + write(0x1); + write >(0x12d); + } + + void ipu_enable() { write(1); write(Gpr2::Data_width_ch1::PX_18_BITS); write(Gpr2::Bit_mapping_ch1::SPWG); write(Gpr2::Ch1_mode::ROUTED_TO_DI1); } + + void buttons_enable() + { + write(1); + write(0); + } }; #endif /* _DRIVERS__PLATFORM__IMX53__IOMUX_H_ */ diff --git a/os/src/drivers/platform/imx53/main.cc b/os/src/drivers/platform/imx53/main.cc index c44f6cd47..43406c14f 100644 --- a/os/src/drivers/platform/imx53/main.cc +++ b/os/src/drivers/platform/imx53/main.cc @@ -58,7 +58,18 @@ class Platform::Session_component : public Genode::Rpc_object case Session::IPU: _src.reset_ipu(); _ccm.ipu_clk_enable(); - _iomux.enable_di1(); + _iomux.ipu_enable(); + break; + case Session::I2C_2: + _ccm.i2c_2_enable(); + _iomux.i2c_2_enable(); + break; + case Session::I2C_3: + _ccm.i2c_3_enable(); + _iomux.i2c_3_enable(); + break; + case Session::BUTTONS: + _iomux.buttons_enable(); break; default: PWRN("Invalid device");