diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hsi.c')
-rw-r--r-- | arch/arm/mach-omap2/omap_hsi.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/omap_hsi.c b/arch/arm/mach-omap2/omap_hsi.c new file mode 100644 index 0000000..ce8fa54 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hsi.c @@ -0,0 +1,426 @@ +/* + * arch/arm/mach-omap2/hsi.c + * + * HSI device definition + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Original Author: Sebastien JAN <s-jan@ti.com> + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/notifier.h> +#include <linux/hsi_driver_if.h> + +#include <asm/clkdev.h> + +#include <plat/omap_hsi.h> +#include <plat/omap_hwmod.h> +#include <plat/omap_device.h> + +#include <../drivers/omap_hsi/hsi_driver.h> +#include "clock.h" +#include "mux.h" +#include "control.h" + +static int omap_hsi_wakeup_enable(int hsi_port); +static int omap_hsi_wakeup_disable(int hsi_port); +#define OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME "omap_hsi" +#define OMAP_HSI_PLATFORM_DEVICE_NAME "omap_hsi.0" +#define OMAP_HSI_HWMOD_NAME "hsi" +#define OMAP_HSI_HWMOD_CLASSNAME "hsi" +#define OMAP_HSI_PADCONF_CAWAKE_PIN "usbb1_ulpitll_clk.hsi1_cawake" +#define OMAP_HSI_PADCONF_CAWAKE_MODE OMAP_MUX_MODE1 + + +#define OMAP_MUX_MODE_MASK 0x7 + + +/* Hack till correct hwmod-mux api gets used */ +#define CA_WAKE_MUX_REG (0x4a1000C2) +#define OMAP44XX_PADCONF_WAKEUPENABLE0 (1 << 14) +#define OMAP44XX_PADCONF_WAKEUPEVENT0 (1 << 15) + +static int omap_mux_read_signal(const char *muxname) +{ + u16 val = 0; + val = omap_readw(CA_WAKE_MUX_REG); + return val; +} + +static int omap_mux_enable_wakeup(const char *muxname) +{ + u16 val = 0; + val = omap_readw(CA_WAKE_MUX_REG); + val |= OMAP44XX_PADCONF_WAKEUPENABLE0; + omap_writew(val, CA_WAKE_MUX_REG); + return 0; +} + +static int omap_mux_disable_wakeup(const char *muxname) +{ + u16 val = 0; + val = omap_readw(CA_WAKE_MUX_REG); + val &= ~OMAP44XX_PADCONF_WAKEUPENABLE0; + omap_writew(val, CA_WAKE_MUX_REG); + return 0; +} + +/* + * NOTE: We abuse a little bit the struct port_ctx to use it also for + * initialization. + */ + + +static struct port_ctx hsi_port_ctx[] = { + [0] = { + .hst.mode = HSI_MODE_FRAME, + .hst.flow = HSI_FLOW_SYNCHRONIZED, + .hst.frame_size = HSI_FRAMESIZE_DEFAULT, + .hst.divisor = HSI_DIVISOR_DEFAULT, + .hst.channels = HSI_CHANNELS_DEFAULT, + .hst.arb_mode = HSI_ARBMODE_ROUNDROBIN, + .hsr.mode = HSI_MODE_FRAME, + .hsr.flow = HSI_FLOW_SYNCHRONIZED, + .hsr.frame_size = HSI_FRAMESIZE_DEFAULT, + .hsr.channels = HSI_CHANNELS_DEFAULT, + .hsr.divisor = HSI_DIVISOR_DEFAULT, + .hsr.counters = HSI_COUNTERS_FT_DEFAULT | + HSI_COUNTERS_TB_DEFAULT | + HSI_COUNTERS_FB_DEFAULT, + }, +}; + +static struct ctrl_ctx hsi_ctx = { + .sysconfig = 0, + .gdd_gcr = 0, + .dll = 0, + .pctx = hsi_port_ctx, +}; + +static struct hsi_platform_data omap_hsi_platform_data = { + .num_ports = ARRAY_SIZE(hsi_port_ctx), + .hsi_gdd_chan_count = HSI_HSI_DMA_CHANNEL_MAX, + .default_hsi_fclk = HSI_DEFAULT_FCLK, + .ctx = &hsi_ctx, + .device_enable = omap_device_enable, + .device_idle = omap_device_idle, + .device_shutdown = omap_device_shutdown, + .wakeup_enable = omap_hsi_wakeup_enable, + .wakeup_disable = omap_hsi_wakeup_disable, + .wakeup_is_from_hsi = omap_hsi_is_io_wakeup_from_hsi, + .board_suspend = omap_hsi_prepare_suspend, +}; + + +static struct platform_device *hsi_get_hsi_platform_device(void) +{ + struct device *dev; + struct platform_device *pdev; + + /* HSI_TODO: handle platform device id (or port) (0/1) */ + dev = bus_find_device_by_name(&platform_bus_type, NULL, + OMAP_HSI_PLATFORM_DEVICE_NAME); + if (!dev) { + pr_debug("Could not find platform device %s\n", + OMAP_HSI_PLATFORM_DEVICE_NAME); + return 0; + } + + if (!dev->driver) { + /* Could not find driver for platform device. */ + return 0; + } + + pdev = to_platform_device(dev); + + return pdev; +} + +static struct hsi_dev *hsi_get_hsi_controller_data(struct platform_device *pd) +{ + struct hsi_dev *hsi_ctrl; + + if (!pd) + return 0; + + hsi_ctrl = (struct hsi_dev *) platform_get_drvdata(pd); + if (!hsi_ctrl) { + pr_err("Could not find HSI controller data\n"); + return 0; + } + + return hsi_ctrl; +} + +/** +* omap_hsi_is_io_pad_hsi - Indicates if IO Pad has been muxed for HSI CAWAKE +* +* Return value :* 0 if CAWAKE Padconf has not been found or CAWAKE not muxed for +* CAWAKE +* * else 1 +*/ +static int omap_hsi_is_io_pad_hsi(void) +{ + u16 val; + + /* Check for IO pad */ + val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN); + if (val == -ENODEV) + return 0; + + /* Continue only if CAWAKE is muxed */ + if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE) + return 0; + + return 1; +} + +/** +* omap_hsi_is_io_wakeup_from_hsi - Indicates an IO wakeup from HSI CAWAKE +* +* Return value :* 0 if CAWAKE Padconf has not been found or no IOWAKEUP event +* occured for CAWAKE +* * else 1 +* TODO : return value should indicate the HSI port which has awaken +*/ +int omap_hsi_is_io_wakeup_from_hsi(void) +{ + u16 val; + + /* Check for IO pad wakeup */ + val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN); + if (val == -ENODEV) + return 0; + + /* Continue only if CAWAKE is muxed */ + if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE) + return 0; + + if (val & OMAP44XX_PADCONF_WAKEUPEVENT0) + return 1; + + return 0; +} + +/** +* omap_hsi_wakeup_enable - Enable HSI wakeup feature from RET/OFF mode +* +* @hsi_port - reference to the HSI port onto which enable wakeup feature. +* +* Return value :* 0 if CAWAKE has been configured to wakeup platform +* * -ENODEV if CAWAKE is not muxed on padconf +*/ +static int omap_hsi_wakeup_enable(int hsi_port) +{ + int ret = -ENODEV; + + if (omap_hsi_is_io_pad_hsi()) + ret = omap_mux_enable_wakeup(OMAP_HSI_PADCONF_CAWAKE_PIN); + else + pr_debug("Trying to enable HSI IO wakeup on non HSI board\n"); + + + /* TODO: handle hsi_port param and use it to find the correct Pad */ + return ret; +} + +/** +* omap_hsi_wakeup_disable - Disable HSI wakeup feature from RET/OFF mode +* +* @hsi_port - reference to the HSI port onto which disable wakeup feature. +* +* Return value :* 0 if CAWAKE has been configured to not wakeup platform +* * -ENODEV if CAWAKE is not muxed on padconf +*/ +static int omap_hsi_wakeup_disable(int hsi_port) +{ + int ret = -ENODEV; + + if (omap_hsi_is_io_pad_hsi()) + ret = omap_mux_disable_wakeup(OMAP_HSI_PADCONF_CAWAKE_PIN); + else + pr_debug("Trying to disable HSI IO wakeup on non HSI board\n"); + + + /* TODO: handle hsi_port param and use it to find the correct Pad */ + + return ret; +} + +/** +* omap_hsi_prepare_suspend - Prepare HSI for suspend mode +* +* Return value :* 0 if CAWAKE padconf has been configured properly +* * -ENODEV if CAWAKE is not muxed on padconf. +* +*/ +int omap_hsi_prepare_suspend(int hsi_port, bool dev_may_wakeup) +{ + int ret; + + if (dev_may_wakeup) + ret = omap_hsi_wakeup_enable(hsi_port); + else + ret = omap_hsi_wakeup_disable(hsi_port); + + return ret; +} + +/** +* omap_hsi_wakeup - Prepare HSI for wakeup from suspend mode (RET/OFF) +* +* Return value : 1 if IO wakeup source is HSI +* 0 if IO wakeup source is not HSI. +*/ +int omap_hsi_wakeup(int hsi_port) +{ + static struct platform_device *pdev; + static struct hsi_dev *hsi_ctrl; + + if (!pdev) { + pdev = hsi_get_hsi_platform_device(); + if (!pdev) + return -ENODEV; +} + + if (!device_may_wakeup(&pdev->dev)) { + dev_info(&pdev->dev, "Modem not allowed to wakeup platform"); + return -EPERM; + } + + if (!hsi_ctrl) { + hsi_ctrl = hsi_get_hsi_controller_data(pdev); + if (!hsi_ctrl) + return -ENODEV; + } + + dev_dbg(hsi_ctrl->dev, "Modem wakeup detected from HSI CAWAKE Pad"); + + /* CAWAKE falling or rising edge detected */ + hsi_ctrl->hsi_port->cawake_off_event = true; + tasklet_hi_schedule(&hsi_ctrl->hsi_port->hsi_tasklet); + + /* Disable interrupt until Bottom Half has cleared */ + /* the IRQ status register */ + disable_irq_nosync(hsi_ctrl->hsi_port->irq); + + return 0; +} + +/* HSI_TODO : This requires some fine tuning & completion of + * activate/deactivate latency values + */ +static struct omap_device_pm_latency omap_hsi_latency[] = { + [0] = { + .deactivate_func = omap_device_idle_hwmods, + .activate_func = omap_device_enable_hwmods, + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, + }, +}; + +/* HSI device registration */ +static int __init omap_hsi_register(struct omap_hwmod *oh, void *user) +{ + struct omap_device *od; + struct hsi_platform_data *pdata = &omap_hsi_platform_data; + + if (!oh) { + pr_err("Could not look up %s omap_hwmod\n", + OMAP_HSI_HWMOD_NAME); + return -EEXIST; + } + + od = omap_device_build(OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME, 0, oh, + pdata, sizeof(*pdata), omap_hsi_latency, + ARRAY_SIZE(omap_hsi_latency), false); + WARN(IS_ERR(od), "Can't build omap_device for %s:%s.\n", + OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME, oh->name); + + pr_info("HSI: device registered as omap_hwmod: %s\n", oh->name); + return 0; +} + +static void __init omap_4430hsi_pad_conf(void) +{ + /* + * HSI pad conf: hsi1_ca/ac_wake/flag/data/ready + * Also configure gpio_92/95/157/187 used by modem + */ + /* hsi1_cawake */ + omap_mux_init_signal("usbb1_ulpitll_clk.hsi1_cawake", \ + OMAP_PIN_INPUT_PULLDOWN | \ + OMAP_PIN_OFF_NONE | \ + OMAP_PIN_OFF_WAKEUPENABLE); + /* hsi1_caflag */ + omap_mux_init_signal("usbb1_ulpitll_dir.hsi1_caflag", \ + OMAP_PIN_INPUT | \ + OMAP_PIN_OFF_NONE); + /* hsi1_cadata */ + omap_mux_init_signal("usbb1_ulpitll_stp.hsi1_cadata", \ + OMAP_PIN_INPUT | \ + OMAP_PIN_OFF_NONE); + /* hsi1_acready */ + omap_mux_init_signal("usbb1_ulpitll_nxt.hsi1_acready", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_OUTPUT_LOW); + /* hsi1_acwake */ + omap_mux_init_signal("usbb1_ulpitll_dat0.hsi1_acwake", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_NONE); + /* hsi1_acdata */ + omap_mux_init_signal("usbb1_ulpitll_dat1.hsi1_acdata", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_NONE); + /* hsi1_acflag */ + omap_mux_init_signal("usbb1_ulpitll_dat2.hsi1_acflag", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_NONE); + /* hsi1_caready */ + omap_mux_init_signal("usbb1_ulpitll_dat3.hsi1_caready", \ + OMAP_PIN_INPUT | \ + OMAP_PIN_OFF_NONE); + /* gpio_92 */ + omap_mux_init_signal("usbb1_ulpitll_dat4.gpio_92", \ + OMAP_PULL_ENA); + /* gpio_95 */ + omap_mux_init_signal("usbb1_ulpitll_dat7.gpio_95", \ + OMAP_PIN_INPUT_PULLDOWN | \ + OMAP_PIN_OFF_NONE); + /* gpio_157 */ + omap_mux_init_signal("usbb2_ulpitll_clk.gpio_157", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_NONE); + /* gpio_187 */ + omap_mux_init_signal("sys_boot3.gpio_187", \ + OMAP_PIN_OUTPUT | \ + OMAP_PIN_OFF_NONE); +} + +int __init omap_hsi_dev_init(void) +{ + /* Keep this for genericity, although there is only one hwmod for HSI */ + return omap_hwmod_for_each_by_class(OMAP_HSI_HWMOD_CLASSNAME, + omap_hsi_register, NULL); +} +postcore_initcall(omap_hsi_dev_init); + +/* HSI devices registration */ +int __init omap_hsi_init(void) +{ + omap_4430hsi_pad_conf(); + return 0; +} |