From patchwork Mon Aug 18 22:23:29 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lina Iyer X-Patchwork-Id: 35542 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ob0-f197.google.com (mail-ob0-f197.google.com [209.85.214.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 8A0012358C for ; Mon, 18 Aug 2014 22:24:00 +0000 (UTC) Received: by mail-ob0-f197.google.com with SMTP id vb8sf39053835obc.0 for ; Mon, 18 Aug 2014 15:24:00 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=Vn1qt+AOPIt5WfSveO2fdGAUlEujlcYo+suVAyAsiQ4=; b=lnpvNR11fFfiIvBeEduQs5i7GKV/YimbsHNVitMAoGkIArPPgd9baOBjISS7zVfcvm clkENRzNbfA6mnBhBu0ywmscShZu9CA8VpbwkluZKIeTzVYut5y13XwYYgB0kuSpXb2E 2K6fm42l/6U1gBs884tHw9U9Akr8tXNpdGpFUOs5BZGZU9Eb+M8KH4H0Fx0lC9wsCyim ggIlWYPiZrD42QeyvhTt+fUkKDF+vaCp3mhSHK1A2MsS5jDBfM3oVVFR7ferXkzJdC4A EfED7EJslhPYDb+/XRYquwRmdJ/vw/Hh9n3W9amC4Grgh1JqF5nnvNoXl7d4kCelHYUP CoDQ== X-Gm-Message-State: ALoCoQkduMypjcSaqxVCI5Diq7/v6R/OUju9Z4f3kZR3HDTHqOx+lfQAkg0dC86MxXHYeW7MWuCE X-Received: by 10.50.117.10 with SMTP id ka10mr1185772igb.1.1408400640121; Mon, 18 Aug 2014 15:24:00 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.88.6 with SMTP id s6ls2594309qgd.22.gmail; Mon, 18 Aug 2014 15:24:00 -0700 (PDT) X-Received: by 10.220.114.5 with SMTP id c5mr27036129vcq.28.1408400639995; Mon, 18 Aug 2014 15:23:59 -0700 (PDT) Received: from mail-vc0-f178.google.com (mail-vc0-f178.google.com [209.85.220.178]) by mx.google.com with ESMTPS id or10si2229261vcb.29.2014.08.18.15.23.59 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 18 Aug 2014 15:23:59 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) client-ip=209.85.220.178; Received: by mail-vc0-f178.google.com with SMTP id la4so6448465vcb.23 for ; Mon, 18 Aug 2014 15:23:59 -0700 (PDT) X-Received: by 10.221.68.66 with SMTP id xx2mr26993492vcb.1.1408400639894; Mon, 18 Aug 2014 15:23:59 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp189778vcb; Mon, 18 Aug 2014 15:23:59 -0700 (PDT) X-Received: by 10.70.46.10 with SMTP id r10mr16193697pdm.9.1408400638551; Mon, 18 Aug 2014 15:23:58 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id pc2si23819953pac.118.2014.08.18.15.23.57 for ; Mon, 18 Aug 2014 15:23:58 -0700 (PDT) Received-SPF: none (google.com: linux-arm-msm-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752404AbaHRWX5 (ORCPT + 5 others); Mon, 18 Aug 2014 18:23:57 -0400 Received: from mail-pa0-f46.google.com ([209.85.220.46]:40007 "EHLO mail-pa0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752352AbaHRWX4 (ORCPT ); Mon, 18 Aug 2014 18:23:56 -0400 Received: by mail-pa0-f46.google.com with SMTP id lj1so8567584pab.33 for ; Mon, 18 Aug 2014 15:23:55 -0700 (PDT) X-Received: by 10.70.134.205 with SMTP id pm13mr17302888pdb.80.1408400635219; Mon, 18 Aug 2014 15:23:55 -0700 (PDT) Received: from ubuntu.localdomain (proxy6-global253.qualcomm.com. [199.106.103.253]) by mx.google.com with ESMTPSA id nn3sm25508125pdb.58.2014.08.18.15.23.53 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 18 Aug 2014 15:23:54 -0700 (PDT) From: Lina Iyer To: daniel.lezcano@linaro.org, khilman@linaro.org, sboyd@codeaurora.org, davidb@codeaurora.org, galak@codeaurora.org, linux-arm-msm@vger.kernel.org, lorenzo.pieralisi@arm.com Cc: msivasub@codeauorora.org, Lina Iyer , Praveen Chidambaram Subject: [PATCH v3 3/8] qcom: spm: Add Subsystem Power Manager driver (SAW2) Date: Mon, 18 Aug 2014 16:23:29 -0600 Message-Id: <1408400614-45419-4-git-send-email-lina.iyer@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1408400614-45419-1-git-send-email-lina.iyer@linaro.org> References: <1408400614-45419-1-git-send-email-lina.iyer@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: lina.iyer@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , SPM is a hardware block that controls the peripheral logic surrounding the application cores (cpu/l$). When the core executes WFI instruction, the SPM takes over the putting the core in low power state as configured. The wake up for the SPM is an interrupt at the GIC, which then completes the rest of low power mode sequence and brings the core out of low power mode. Allow drivers to configure the idle mode for the cores in the SPM start address register. Signed-off-by: Praveen Chidambaram Signed-off-by: Lina Iyer --- drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/spm.c | 176 ++++++++++++++++++++++++++++++++++++++++++ drivers/soc/qcom/spm_driver.h | 85 ++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 drivers/soc/qcom/spm.c create mode 100644 drivers/soc/qcom/spm_driver.h diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 70d52ed..20b329f 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o +obj-$(CONFIG_QCOM_PM) += spm.o CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c new file mode 100644 index 0000000..3e1de43 --- /dev/null +++ b/drivers/soc/qcom/spm.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "spm_driver.h" + +#define NUM_SPM_ENTRY 32 + +static uint32_t msm_spm_reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = { + [MSM_SPM_REG_SAW2_SECURE] = 0x00, + [MSM_SPM_REG_SAW2_ID] = 0x04, + [MSM_SPM_REG_SAW2_CFG] = 0x08, + [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C, + [MSM_SPM_REG_SAW2_AVS_STS] = 0x10, + [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14, + [MSM_SPM_REG_SAW2_RST] = 0x18, + [MSM_SPM_REG_SAW2_VCTL] = 0x1C, + [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20, + [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24, + [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28, + [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C, + [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30, + [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34, + [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40, + [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44, + [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48, + [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C, + [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50, + [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54, + [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58, + [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C, + [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80, + [MSM_SPM_REG_SAW2_VERSION] = 0xFD0, +}; + +static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev, + unsigned int reg_index) +{ + __raw_writel(dev->reg_shadow[reg_index], + dev->reg_base_addr + dev->reg_offsets[reg_index]); +} + +static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev, + unsigned int reg_index) +{ + dev->reg_shadow[reg_index] = __raw_readl(dev->reg_base_addr + + dev->reg_offsets[reg_index]); +} + +static inline void msm_spm_drv_set_start_addr( + struct msm_spm_driver_data *dev, uint32_t addr) +{ + addr &= 0x7F; + addr <<= 4; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr; +} + +inline int msm_spm_drv_set_spm_enable( + struct msm_spm_driver_data *dev, bool enable) +{ + uint32_t value = enable ? 0x01 : 0x00; + + if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) { + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value; + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); + wmb(); + } + + return 0; +} + +void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev) +{ + int i; + + for (i = 0; i < NUM_SPM_ENTRY; i++) { + __raw_writel(dev->reg_seq_entry_shadow[i], + dev->reg_base_addr + + dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY] + + 4 * i); + } + mb(); +} + +/** + * msm_spm_drv_write_seq_data - Load the SPM register with sequences + * + * @dev - The SPM device whose sequences to be programmed + * @cmd - The byte array + * @offset - The last written byte position, to continue from. + */ +int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev, + uint8_t *cmd, uint32_t *offset) +{ + uint32_t cmd_w; + uint32_t offset_w = *offset / 4; + uint8_t last_cmd; + + while (1) { + int i; + + cmd_w = 0; + last_cmd = 0; + cmd_w = dev->reg_seq_entry_shadow[offset_w]; + + for (i = (*offset % 4); i < 4; i++) { + last_cmd = *(cmd++); + cmd_w |= last_cmd << (i * 8); + (*offset)++; + if (last_cmd == 0x0f) + break; + } + + dev->reg_seq_entry_shadow[offset_w++] = cmd_w; + if (last_cmd == 0x0f) + break; + } + + return 0; +} + +int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev, + uint32_t addr) +{ + + msm_spm_drv_set_start_addr(dev, addr); + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); + wmb(); + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS); + + return 0; +} + +void msm_spm_drv_reinit(struct msm_spm_driver_data *dev) +{ + int i; + + msm_spm_drv_flush_seq_entry(dev); + + for (i = MSM_SPM_REG_NR_INITIALIZE + 1; i < MSM_SPM_REG_NR; i++) + msm_spm_drv_load_shadow(dev, i); +} + +int msm_spm_drv_init(struct msm_spm_driver_data *dev, + struct msm_spm_platform_data *data) +{ + dev->reg_base_addr = data->reg_base_addr; + memcpy(dev->reg_shadow, data->reg_init_values, + sizeof(data->reg_init_values)); + dev->reg_offsets = msm_spm_reg_offsets_saw2_v2_1; + dev->reg_seq_entry_shadow = kcalloc(NUM_SPM_ENTRY, + sizeof(*dev->reg_seq_entry_shadow), + GFP_KERNEL); + if (!dev->reg_seq_entry_shadow) + return -ENOMEM; + + return 0; +} diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h new file mode 100644 index 0000000..6ff69bb --- /dev/null +++ b/drivers/soc/qcom/spm_driver.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __QCOM_SPM_DRIVER_H +#define __QCOM_SPM_DRIVER_H + +enum { + MSM_SPM_REG_SAW2_CFG, + MSM_SPM_REG_SAW2_AVS_CTL, + MSM_SPM_REG_SAW2_AVS_HYSTERESIS, + MSM_SPM_REG_SAW2_SPM_CTL, + MSM_SPM_REG_SAW2_PMIC_DLY, + MSM_SPM_REG_SAW2_AVS_LIMIT, + MSM_SPM_REG_SAW2_AVS_DLY, + MSM_SPM_REG_SAW2_SPM_DLY, + MSM_SPM_REG_SAW2_PMIC_DATA_0, + MSM_SPM_REG_SAW2_PMIC_DATA_1, + MSM_SPM_REG_SAW2_PMIC_DATA_2, + MSM_SPM_REG_SAW2_PMIC_DATA_3, + MSM_SPM_REG_SAW2_PMIC_DATA_4, + MSM_SPM_REG_SAW2_PMIC_DATA_5, + MSM_SPM_REG_SAW2_PMIC_DATA_6, + MSM_SPM_REG_SAW2_PMIC_DATA_7, + MSM_SPM_REG_SAW2_RST, + + MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST, + + MSM_SPM_REG_SAW2_ID, + MSM_SPM_REG_SAW2_SECURE, + MSM_SPM_REG_SAW2_STS0, + MSM_SPM_REG_SAW2_STS1, + MSM_SPM_REG_SAW2_STS2, + MSM_SPM_REG_SAW2_VCTL, + MSM_SPM_REG_SAW2_SEQ_ENTRY, + MSM_SPM_REG_SAW2_SPM_STS, + MSM_SPM_REG_SAW2_AVS_STS, + MSM_SPM_REG_SAW2_PMIC_STS, + MSM_SPM_REG_SAW2_VERSION, + + MSM_SPM_REG_NR, +}; + +struct msm_spm_seq_entry { + uint32_t mode; + uint8_t *cmd; +}; + +struct msm_spm_platform_data { + void __iomem *reg_base_addr; + uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE]; + + uint32_t num_modes; + struct msm_spm_seq_entry *modes; +}; + +struct msm_spm_driver_data { + void __iomem *reg_base_addr; + uint32_t reg_shadow[MSM_SPM_REG_NR]; + uint32_t *reg_seq_entry_shadow; + uint32_t *reg_offsets; + uint32_t num_spm_entry; +}; + +int msm_spm_drv_init(struct msm_spm_driver_data *dev, + struct msm_spm_platform_data *data); +void msm_spm_drv_reinit(struct msm_spm_driver_data *dev); +int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev, + uint32_t addr); +int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev, + uint8_t *cmd, uint32_t *offset); +void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev); +int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev, + bool enable); +void msm_spm_reinit(void); +int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs); + +#endif /* __QCOM_SPM_DRIVER_H */