Message ID | 1401211721-19712-3-git-send-email-bjorn.andersson@sonymobile.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Hi Bjorn, On 27/05/14 18:28, Bjorn Andersson wrote: > Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960 and > 8064 based devices. The driver exposes resources that child drivers can operate > on; to implementing regulator, clock and bus frequency drivers. > > Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> > --- > drivers/mfd/Kconfig | 15 ++ > drivers/mfd/Makefile | 1 + > drivers/mfd/qcom_rpm.c | 575 +++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/qcom_rpm.h | 12 + > 4 files changed, 603 insertions(+) > create mode 100644 drivers/mfd/qcom_rpm.c > create mode 100644 include/linux/mfd/qcom_rpm.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index 3383412..e5122a7 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -482,6 +482,21 @@ config UCB1400_CORE > To compile this driver as a module, choose M here: the > module will be called ucb1400_core. > > +config MFD_QCOM_RPM > + tristate "Qualcomm Resource Power Manager (RPM) > + depends on ARCH_QCOM && OF > + select MFD_CORE > + help > + If you say yes to this option, support will be included for the > + Resource Power Manager system found in the Qualcomm 8660, 8960 and > + 8064 based devices. > + > + This is required to access many regulators, clocks and bus > + frequencies controlled by the RPM on these devices. > + > + Say M here if you want to include support for the Qualcomm RPM as a > + module. This will build a module called "qcom_rpm". > + > config MFD_PM8XXX > tristate > > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index 2851275..bbe4209 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -150,6 +150,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o > > obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o > obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o > +obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o > obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o > obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o > obj-$(CONFIG_MFD_TPS65090) += tps65090.o > diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c > new file mode 100644 > index 0000000..96135ab > --- /dev/null > +++ b/drivers/mfd/qcom_rpm.c > @@ -0,0 +1,575 @@ > +/* > + * Copyright (c) 2014, Sony Mobile Communications AB. > + * Copyright (c) 2013, 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 <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/of_platform.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > + > +#include <linux/mfd/qcom_rpm.h> > + > +#include <dt-bindings/mfd/qcom_rpm.h> > + > +struct qcom_rpm_resource { > + unsigned target_id; > + unsigned status_id; > + unsigned select_id; > + unsigned size; > +}; > + > +struct qcom_rpm { > + struct device *dev; > + struct completion ack; > + struct mutex lock; > + > + void __iomem *status_regs; > + void __iomem *ctrl_regs; > + void __iomem *req_regs; > + > + void __iomem *ipc_rpm_reg; > + > + u32 ack_status; > + > + u32 version; > + > + const struct qcom_rpm_resource *resource_table; > + unsigned n_resources; the line spacing looks bit odd. > +}; > + > +#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) > +#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) > +#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) Probably you could make these macros bit more generic by removing the rpm and let the calling code dereference it. > + > +#define RPM_REQUEST_TIMEOUT (5 * HZ) > + > +#define RPM_REQUEST_CONTEXT 3 > +#define RPM_REQ_SELECT 11 > +#define RPM_ACK_CONTEXT 15 > +#define RPM_ACK_SELECTOR 23 > +#define RPM_SELECT_SIZE 7 > + > +#define RPM_ACTIVE_STATE BIT(0) > +#define RPM_NOTIFICATION BIT(30) > +#define RPM_REJECTED BIT(31) > + > +#define RPM_SIGNAL BIT(2) > + > +static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = { > + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, > + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, > + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, > + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, > + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, > + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, > + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, > + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, > + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, > + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, > + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, > + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, > + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, > + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, > + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, > + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, > + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, > + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, > + [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 }, > + [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 }, > + [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, > + [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 }, > + [QCOM_RPM_PM8921_S1] = { 116, 31, 30, 2 }, > + [QCOM_RPM_PM8921_S2] = { 118, 33, 31, 2 }, > + [QCOM_RPM_PM8921_S3] = { 120, 35, 32, 2 }, > + [QCOM_RPM_PM8921_S4] = { 122, 37, 33, 2 }, > + [QCOM_RPM_PM8921_S5] = { 124, 39, 34, 2 }, > + [QCOM_RPM_PM8921_S6] = { 126, 41, 35, 2 }, > + [QCOM_RPM_PM8921_S7] = { 128, 43, 36, 2 }, > + [QCOM_RPM_PM8921_S8] = { 130, 45, 37, 2 }, > + [QCOM_RPM_PM8921_L1] = { 132, 47, 38, 2 }, > + [QCOM_RPM_PM8921_L2] = { 134, 49, 39, 2 }, > + [QCOM_RPM_PM8921_L3] = { 136, 51, 40, 2 }, > + [QCOM_RPM_PM8921_L4] = { 138, 53, 41, 2 }, > + [QCOM_RPM_PM8921_L5] = { 140, 55, 42, 2 }, > + [QCOM_RPM_PM8921_L6] = { 142, 57, 43, 2 }, > + [QCOM_RPM_PM8921_L7] = { 144, 59, 44, 2 }, > + [QCOM_RPM_PM8921_L8] = { 146, 61, 45, 2 }, > + [QCOM_RPM_PM8921_L9] = { 148, 63, 46, 2 }, > + [QCOM_RPM_PM8921_L10] = { 150, 65, 47, 2 }, > + [QCOM_RPM_PM8921_L11] = { 152, 67, 48, 2 }, > + [QCOM_RPM_PM8921_L12] = { 154, 69, 49, 2 }, > + [QCOM_RPM_PM8921_L13] = { 156, 71, 50, 2 }, > + [QCOM_RPM_PM8921_L14] = { 158, 73, 51, 2 }, > + [QCOM_RPM_PM8921_L15] = { 160, 75, 52, 2 }, > + [QCOM_RPM_PM8921_L16] = { 162, 77, 53, 2 }, > + [QCOM_RPM_PM8921_L17] = { 164, 79, 54, 2 }, > + [QCOM_RPM_PM8921_L18] = { 166, 81, 55, 2 }, > + [QCOM_RPM_PM8921_L19] = { 168, 83, 56, 2 }, > + [QCOM_RPM_PM8921_L20] = { 170, 85, 57, 2 }, > + [QCOM_RPM_PM8921_L21] = { 172, 87, 58, 2 }, > + [QCOM_RPM_PM8921_L22] = { 174, 89, 59, 2 }, > + [QCOM_RPM_PM8921_L23] = { 176, 91, 60, 2 }, > + [QCOM_RPM_PM8921_L24] = { 178, 93, 61, 2 }, > + [QCOM_RPM_PM8921_L25] = { 180, 95, 62, 2 }, > + [QCOM_RPM_PM8921_L26] = { 182, 97, 63, 2 }, > + [QCOM_RPM_PM8921_L27] = { 184, 99, 64, 2 }, > + [QCOM_RPM_PM8921_L28] = { 186, 101, 65, 2 }, > + [QCOM_RPM_PM8921_L29] = { 188, 103, 66, 2 }, > + [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 }, > + [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 }, > + [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 }, > + [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 }, > + [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 }, > + [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 }, > + [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 }, > + [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 }, > + [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 }, > + [QCOM_RPM_PM8821_S1] = { 201, 116, 76, 2 }, > + [QCOM_RPM_PM8821_S2] = { 203, 118, 77, 2 }, > + [QCOM_RPM_PM8821_L1] = { 205, 120, 78, 2 }, > + [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 }, > + [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 }, > + [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 }, > + [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 }, > + [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 }, > + [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 }, > +}; > + > +static const struct qcom_rpm apq8064_template = { > + .version = 3, > + .resource_table = apq8064_rpm_resource_table, > + .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table), > +}; > + > +static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = { > + [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 }, > + [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 }, > + [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 }, > + [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 }, > + [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 }, > + [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 }, > + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 }, > + [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 }, > + [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 }, > + [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 }, > + [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 }, > + [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 }, > + [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 }, > + [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 }, > + [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 }, > + [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 }, > + [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 }, > + [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 }, > + [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 }, > + [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 }, > + [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 }, > + [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 }, > + [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 }, > + [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 }, > + [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 }, > + [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 }, > + [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 }, > + [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 }, > + [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 }, > + [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 }, > + [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 }, > + [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 }, > + [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 }, > + [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 }, > + [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 }, > + [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 }, > + [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 }, > + [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 }, > + [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 }, > + [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 }, > + [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 }, > + [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 }, > + [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 }, > + [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 }, > + [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 }, > + [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 }, > + [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 }, > + [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 }, > + [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 }, > + [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 }, > + [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 }, > + [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 }, > + [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 }, > + [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 }, > + [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 }, > + [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 }, > + [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 }, > + [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 }, > + [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 }, > + [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 }, > + [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 }, > + [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 }, > + [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 }, > + [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 }, > + [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 }, > + [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 }, > + [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 }, > + [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 }, > + [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 }, > + [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 }, > + [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 }, > + [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 }, > + [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 }, > + [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 }, > +}; > + > +static const struct qcom_rpm msm8660_template = { > + .version = -1, > + .resource_table = msm8660_rpm_resource_table, > + .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table), > +}; > + > +static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = { > + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, > + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, > + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, > + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, > + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, > + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, > + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, > + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, > + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, > + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, > + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, > + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, > + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, > + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, > + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, > + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, > + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, > + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 }, > + [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 }, > + [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 }, > + [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 }, > + [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 }, > + [QCOM_RPM_PM8921_S1] = { 117, 31, 30, 2 }, > + [QCOM_RPM_PM8921_S2] = { 119, 33, 31, 2 }, > + [QCOM_RPM_PM8921_S3] = { 121, 35, 32, 2 }, > + [QCOM_RPM_PM8921_S4] = { 123, 37, 33, 2 }, > + [QCOM_RPM_PM8921_S5] = { 125, 39, 34, 2 }, > + [QCOM_RPM_PM8921_S6] = { 127, 41, 35, 2 }, > + [QCOM_RPM_PM8921_S7] = { 129, 43, 36, 2 }, > + [QCOM_RPM_PM8921_S8] = { 131, 45, 37, 2 }, > + [QCOM_RPM_PM8921_L1] = { 133, 47, 38, 2 }, > + [QCOM_RPM_PM8921_L2] = { 135, 49, 39, 2 }, > + [QCOM_RPM_PM8921_L3] = { 137, 51, 40, 2 }, > + [QCOM_RPM_PM8921_L4] = { 139, 53, 41, 2 }, > + [QCOM_RPM_PM8921_L5] = { 141, 55, 42, 2 }, > + [QCOM_RPM_PM8921_L6] = { 143, 57, 43, 2 }, > + [QCOM_RPM_PM8921_L7] = { 145, 59, 44, 2 }, > + [QCOM_RPM_PM8921_L8] = { 147, 61, 45, 2 }, > + [QCOM_RPM_PM8921_L9] = { 149, 63, 46, 2 }, > + [QCOM_RPM_PM8921_L10] = { 151, 65, 47, 2 }, > + [QCOM_RPM_PM8921_L11] = { 153, 67, 48, 2 }, > + [QCOM_RPM_PM8921_L12] = { 155, 69, 49, 2 }, > + [QCOM_RPM_PM8921_L13] = { 157, 71, 50, 2 }, > + [QCOM_RPM_PM8921_L14] = { 159, 73, 51, 2 }, > + [QCOM_RPM_PM8921_L15] = { 161, 75, 52, 2 }, > + [QCOM_RPM_PM8921_L16] = { 163, 77, 53, 2 }, > + [QCOM_RPM_PM8921_L17] = { 165, 79, 54, 2 }, > + [QCOM_RPM_PM8921_L18] = { 167, 81, 55, 2 }, > + [QCOM_RPM_PM8921_L19] = { 169, 83, 56, 2 }, > + [QCOM_RPM_PM8921_L20] = { 171, 85, 57, 2 }, > + [QCOM_RPM_PM8921_L21] = { 173, 87, 58, 2 }, > + [QCOM_RPM_PM8921_L22] = { 175, 89, 59, 2 }, > + [QCOM_RPM_PM8921_L23] = { 177, 91, 60, 2 }, > + [QCOM_RPM_PM8921_L24] = { 179, 93, 61, 2 }, > + [QCOM_RPM_PM8921_L25] = { 181, 95, 62, 2 }, > + [QCOM_RPM_PM8921_L26] = { 183, 97, 63, 2 }, > + [QCOM_RPM_PM8921_L27] = { 185, 99, 64, 2 }, > + [QCOM_RPM_PM8921_L28] = { 187, 101, 65, 2 }, > + [QCOM_RPM_PM8921_L29] = { 189, 103, 66, 2 }, > + [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 }, > + [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 }, > + [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 }, > + [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 }, > + [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 }, > + [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 }, > + [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 }, > + [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 }, > + [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 }, > + [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 }, > + [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 }, > + [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 }, > + [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 }, > + [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 }, > +}; > + > +static const struct qcom_rpm msm8960_template = { > + .version = 3, > + .resource_table = msm8960_rpm_resource_table, > + .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), > +}; > + > +static const struct of_device_id qcom_rpm_of_match[] = { > + { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, > + { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, > + { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); > + > +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev) > +{ > + return dev_get_drvdata(dev); > +} > +EXPORT_SYMBOL(dev_get_qcom_rpm); > + > +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count) > +{ > + const struct qcom_rpm_resource *res; > + u32 sel_mask[RPM_SELECT_SIZE] = { 0 }; > + int left; > + int ret = 0; > + int i; > + > + if (WARN_ON(resource < 0 || resource >= rpm->n_resources)) > + return -EINVAL; > + > + res = &rpm->resource_table[resource]; > + if (WARN_ON(res->size != count)) > + return -EINVAL; > + > + mutex_lock(&rpm->lock); > + > + for (i = 0; i < res->size; i++) > + writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i)); > + > + bitmap_set((unsigned long *)sel_mask, res->select_id, 1); > + for (i = 0; i < ARRAY_SIZE(sel_mask); i++) { > + writel_relaxed(sel_mask[i], > + RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i)); > + } > + > + writel_relaxed(RPM_ACTIVE_STATE, > + RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT)); > + > + reinit_completion(&rpm->ack); > + writel(RPM_SIGNAL, rpm->ipc_rpm_reg); > + > + left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); > + if (!left) > + ret = -ETIMEDOUT; > + else if (rpm->ack_status & RPM_REJECTED) > + ret = -EIO; > + > + mutex_unlock(&rpm->lock); > + > + return ret; > +} > +EXPORT_SYMBOL(qcom_rpm_write); > + > +static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev) > +{ > + struct qcom_rpm *rpm = dev; > + u32 ack; > + int i; > + > + ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); /n > + for (i = 0; i < RPM_SELECT_SIZE; i++) > + writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i)); /n > + writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); > + > + if (ack & RPM_NOTIFICATION) { > + dev_warn(rpm->dev, "ignoring notification!\n"); > + } else { > + rpm->ack_status = ack; > + complete(&rpm->ack); > + } > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev) > +{ > + struct qcom_rpm *rpm = dev; > + > + writel(0x1, rpm->ipc_rpm_reg); > + > + dev_err(rpm->dev, "RPM triggered fatal error\n"); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev) > +{ > + return IRQ_HANDLED; > +} > + > +static int qcom_rpm_probe(struct platform_device *pdev) > +{ > + const struct of_device_id *match; > + const struct qcom_rpm *template; > + struct resource *res; > + struct qcom_rpm *rpm; > + u32 fw_version[3]; > + int irq_wakeup; > + int irq_ack; > + int irq_err; > + int ret; > + > + rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); Sorry If I missed somthing obvious, But why not just use the structure from of_data. Is the global structure going to be used for something else? Or make a seperate structure for of_data and not use struct qcom_rpm? > + if (!rpm) { > + dev_err(&pdev->dev, "Can't allocate qcom_rpm\n"); message not necessary as kernel will print the alocation failures. > + return -ENOMEM; > + } > + rpm->dev = &pdev->dev; > + mutex_init(&rpm->lock); > + init_completion(&rpm->ack); > + > + irq_ack = platform_get_irq_byname(pdev, "ack"); > + if (irq_ack < 0) { > + dev_err(&pdev->dev, "required ack interrupt missing\n"); > + return irq_ack; > + } > + > + irq_err = platform_get_irq_byname(pdev, "err"); > + if (irq_err < 0) { > + dev_err(&pdev->dev, "required err interrupt missing\n"); > + return irq_err; > + } > + > + irq_wakeup = platform_get_irq_byname(pdev, "wakeup"); > + if (irq_wakeup < 0) { > + dev_err(&pdev->dev, "required wakeup interrupt missing\n"); > + return irq_wakeup; > + } > + > + match = of_match_device(qcom_rpm_of_match, &pdev->dev); > + template = match->data; > + > + rpm->version = template->version; > + rpm->resource_table = template->resource_table; > + rpm->n_resources = template->n_resources; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); Should't you use the platform_get_resource_byname here? missed error case checks too. > + rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); > + rpm->ctrl_regs = rpm->status_regs + 0x400; > + rpm->req_regs = rpm->status_regs + 0x600; > + if (IS_ERR(rpm->status_regs)) > + return PTR_ERR(rpm->status_regs); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); Dito. > + rpm->ipc_rpm_reg = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(rpm->ipc_rpm_reg)) > + return PTR_ERR(rpm->ipc_rpm_reg); > + > + dev_set_drvdata(&pdev->dev, rpm); > + > + fw_version[0] = readl(RPM_STATUS_REG(rpm, 0)); > + fw_version[1] = readl(RPM_STATUS_REG(rpm, 1)); > + fw_version[2] = readl(RPM_STATUS_REG(rpm, 2)); > + if (fw_version[0] != rpm->version) { > + dev_err(&pdev->dev, > + "RPM version %u.%u.%u incompatible with driver version %u", > + fw_version[0], > + fw_version[1], > + fw_version[2], > + rpm->version); > + return -EFAULT; > + } > + > + dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0], > + fw_version[1], > + fw_version[2]); > + > + writel(fw_version[0], RPM_CTRL_REG(rpm, 0)); > + writel(fw_version[1], RPM_CTRL_REG(rpm, 1)); > + writel(fw_version[2], RPM_CTRL_REG(rpm, 2)); > + > + ret = devm_request_irq(&pdev->dev, > + irq_ack, > + qcom_rpm_ack_interrupt, > + IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, > + "qcom_rpm_ack", > + rpm); > + if (ret) { > + dev_err(&pdev->dev, "failed to request ack interrupt\n"); > + return ret; > + } > + [ .. > + ret = irq_set_irq_wake(irq_ack, 1); > + if (ret) > + dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n"); > + ..] Shouln't these be set as part of the pm suspend call, if the device is wakeup capable? > + ret = devm_request_irq(&pdev->dev, > + irq_err, > + qcom_rpm_err_interrupt, > + IRQF_TRIGGER_RISING, > + "qcom_rpm_err", > + rpm); > + if (ret) { > + dev_err(&pdev->dev, "failed to request err interrupt\n"); > + return ret; > + } > + > + ret = devm_request_irq(&pdev->dev, > + irq_wakeup, > + qcom_rpm_wakeup_interrupt, > + IRQF_TRIGGER_RISING, > + "qcom_rpm_wakeup", > + rpm); > + if (ret) { > + dev_err(&pdev->dev, "failed to request wakeup interrupt\n"); > + return ret; > + } > + > + ret = irq_set_irq_wake(irq_wakeup, 1); > + if (ret) > + dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n"); > + > + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); > +} > + > +static int qcom_rpm_remove_child(struct device *dev, void *unused) > +{ > + platform_device_unregister(to_platform_device(dev)); > + return 0; > +} > + > +static int qcom_rpm_remove(struct platform_device *pdev) > +{ > + device_for_each_child(&pdev->dev, NULL, qcom_rpm_remove_child); > + return 0; > +} > + > +static struct platform_driver qcom_rpm_driver = { > + .probe = qcom_rpm_probe, > + .remove = qcom_rpm_remove, > + .driver = { > + .name = "qcom_rpm", > + .owner = THIS_MODULE, > + .of_match_table = qcom_rpm_of_match, > + }, > +}; > + > +static int __init qcom_rpm_init(void) > +{ > + return platform_driver_register(&qcom_rpm_driver); > +} > +arch_initcall(qcom_rpm_init); > + > +static void __exit qcom_rpm_exit(void) > +{ > + platform_driver_unregister(&qcom_rpm_driver); > +} > +module_exit(qcom_rpm_exit) > + > +MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h > new file mode 100644 > index 0000000..a52bc37 > --- /dev/null > +++ b/include/linux/mfd/qcom_rpm.h > @@ -0,0 +1,12 @@ > +#ifndef __QCOM_RPM_H__ > +#define __QCOM_RPM_H__ > + > +#include <linux/types.h> > + > +struct device; > +struct qcom_rpm; > + > +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev); > +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count); IMHO, dummy functions for these are required, otherwise you would get a compilation errors on client drivers. > + > +#endif > thanks, srini -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, May 29, 2014 at 9:19 AM, Srinivas Kandagatla <srinivas.kandagatla@linaro.org> wrote: >> +++ b/drivers/mfd/qcom_rpm.c [...] >> +struct qcom_rpm { >> + struct device *dev; >> + struct completion ack; >> + struct mutex lock; >> + >> + void __iomem *status_regs; >> + void __iomem *ctrl_regs; >> + void __iomem *req_regs; >> + >> + void __iomem *ipc_rpm_reg; >> + >> + u32 ack_status; >> + >> + u32 version; >> + >> + const struct qcom_rpm_resource *resource_table; >> + unsigned n_resources; > > > the line spacing looks bit odd. > I'll fix >> +}; >> + >> +#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) >> +#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) >> +#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) > > > Probably you could make these macros bit more generic by removing the rpm > and let the calling code dereference it. > > I first open coded them, I then had separate writel/readl wrappers for them and then I settled for this, as I figured it help clarifying the code. I can have another look at it, but I don't think that below will make things clearer. #define RPM_IDX_2_OFFSET(i) ((i) * 4) [...] >> + >> +static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev) >> +{ >> + struct qcom_rpm *rpm = dev; >> + u32 ack; >> + int i; >> + >> + ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); > > /n > Will try it out. >> + for (i = 0; i < RPM_SELECT_SIZE; i++) >> + writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + >> i)); > > > /n > Will try it out, although to me this grouping says "write all selectors and the context". >> + writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); >> + >> + if (ack & RPM_NOTIFICATION) { >> + dev_warn(rpm->dev, "ignoring notification!\n"); >> + } else { >> + rpm->ack_status = ack; >> + complete(&rpm->ack); >> + } >> + >> + return IRQ_HANDLED; >> +} [...] >> +static int qcom_rpm_probe(struct platform_device *pdev) >> +{ >> + const struct of_device_id *match; >> + const struct qcom_rpm *template; >> + struct resource *res; >> + struct qcom_rpm *rpm; >> + u32 fw_version[3]; >> + int irq_wakeup; >> + int irq_ack; >> + int irq_err; >> + int ret; >> + >> + rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); > > > Sorry If I missed somthing obvious, But why not just use the structure from > of_data. Is the global structure going to be used for something else? > > Or make a seperate structure for of_data and not use struct qcom_rpm? > > > Although we will not have more than one rpm in a system and therefore not instatiate this driver multiple times I do not want to run it off the global state. >> + if (!rpm) { >> + dev_err(&pdev->dev, "Can't allocate qcom_rpm\n"); > > message not necessary as kernel will print the alocation failures. > Thanks! >> + return -ENOMEM; >> + } [...] >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > Should't you use the platform_get_resource_byname here? > > missed error case checks too. > This is a fairly commonly used construct, to have the error from platform_get_resource being propagated through devm_ioremap_resource and catch it there. It gives an extra error print in the log, but I find it very clean. >> + rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); >> + rpm->ctrl_regs = rpm->status_regs + 0x400; >> + rpm->req_regs = rpm->status_regs + 0x600; >> + if (IS_ERR(rpm->status_regs)) >> + return PTR_ERR(rpm->status_regs); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); > > Dito. > [...] > > > [ .. > >> + ret = irq_set_irq_wake(irq_ack, 1); >> + if (ret) >> + dev_warn(&pdev->dev, "failed to mark ack irq as >> wakeup\n"); >> + > > ..] > > Shouln't these be set as part of the pm suspend call, if the device is > wakeup capable? > > Is there any reason to toggle this? I'm not sure when this interrupt will actually be fired, but I don't see any harm in keeping it wakup enabled at all times. [...] >> +++ b/include/linux/mfd/qcom_rpm.h >> @@ -0,0 +1,12 @@ >> +#ifndef __QCOM_RPM_H__ >> +#define __QCOM_RPM_H__ >> + >> +#include <linux/types.h> >> + >> +struct device; >> +struct qcom_rpm; >> + >> +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev); >> +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t >> count); > > > IMHO, dummy functions for these are required, otherwise you would get a > compilation errors on client drivers. > I didn't expect us to compile the children into a kernel that doesn't have the rpm, as I see them as one entity. An exception would be if we want to add COMPILE_TEST to the children, but that would require an extra change anyways. Thanks for the review! Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 29/05/14 20:45, Bjorn Andersson wrote: > On Thu, May 29, 2014 at 9:19 AM, Srinivas Kandagatla > <srinivas.kandagatla@linaro.org> wrote: >>> +++ b/drivers/mfd/qcom_rpm.c > >> the line spacing looks bit odd. >> > > I'll fix > >>> +}; >>> + >>> +#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) >>> +#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) >>> +#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) >> >> >> Probably you could make these macros bit more generic by removing the rpm >> and let the calling code dereference it. >> >> > > I first open coded them, I then had separate writel/readl wrappers for them and > then I settled for this, as I figured it help clarifying the code. I can have > another look at it, but I don't think that below will make things clearer. > > #define RPM_IDX_2_OFFSET(i) ((i) * 4) > Yes, just leave it as it is. > [...] > >> >>> +static int qcom_rpm_probe(struct platform_device *pdev) >>> +{ >>> + const struct of_device_id *match; >>> + const struct qcom_rpm *template; >>> + struct resource *res; >>> + struct qcom_rpm *rpm; >>> + u32 fw_version[3]; >>> + int irq_wakeup; >>> + int irq_ack; >>> + int irq_err; >>> + int ret; >>> + >>> + rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); >> >> >> Sorry If I missed somthing obvious, But why not just use the structure from >> of_data. Is the global structure going to be used for something else? >> >> Or make a seperate structure for of_data and not use struct qcom_rpm? >> >> >> > > Although we will not have more than one rpm in a system and therefore not > instatiate this driver multiple times I do not want to run it off the global > state. > I agree. Why not make a separate data structure for the qcom_of_data? >>> + if (!rpm) { >>> + dev_err(&pdev->dev, "Can't allocate qcom_rpm\n"); >> >> message not necessary as kernel will print the alocation failures. >> > > Thanks! > >>> + return -ENOMEM; >>> + } > > [...] > >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> >> Should't you use the platform_get_resource_byname here? >> >> missed error case checks too. >> > > This is a fairly commonly used construct, to have the error from > platform_get_resource being propagated through devm_ioremap_resource and catch > it there. It gives an extra error print in the log, but I find it very clean. Sorry I missed that point... But my point on platform_get_resource_byname is to remove the dependency on the resource ordering, It is very difficult to catch errors resulting in wrong ordered resources in DT. > >>> + rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); >>> + rpm->ctrl_regs = rpm->status_regs + 0x400; >>> + rpm->req_regs = rpm->status_regs + 0x600; >>> + if (IS_ERR(rpm->status_regs)) >>> + return PTR_ERR(rpm->status_regs); >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); >> >> Dito. >> > > [...] > >> >> >> [ .. >> >>> + ret = irq_set_irq_wake(irq_ack, 1); >>> + if (ret) >>> + dev_warn(&pdev->dev, "failed to mark ack irq as >>> wakeup\n"); >>> + >> >> ..] >> >> Shouln't these be set as part of the pm suspend call, if the device is >> wakeup capable? >> >> > > Is there any reason to toggle this? > > I'm not sure when this interrupt will actually be fired, but I don't see any > harm in keeping it wakup enabled at all times. Typically the wake-up source is driven/enabled by the user. When the system goes to low-power state it would enable the wakeup on the irq. And when there is an interrupt it would wake up the system as part of resuming from low-power state. Again if you what this interrupt to wakeup the system, I would expect pm_wakeup_event/related calls in the interrupt handler too. > > [...] > >>> +++ b/include/linux/mfd/qcom_rpm.h >>> @@ -0,0 +1,12 @@ >>> +#ifndef __QCOM_RPM_H__ >>> +#define __QCOM_RPM_H__ >>> + >>> +#include <linux/types.h> >>> + >>> +struct device; >>> +struct qcom_rpm; >>> + >>> +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev); >>> +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t >>> count); >> >> >> IMHO, dummy functions for these are required, otherwise you would get a >> compilation errors on client drivers. >> > > I didn't expect us to compile the children into a kernel that doesn't have the > rpm, as I see them as one entity. An exception would be if we want to add > COMPILE_TEST to the children, but that would require an extra change anyways. > > Thanks for the review! > NP, Thanks, srini > Regards, > Bjorn > -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, May 29, 2014 at 2:41 PM, Srinivas Kandagatla <srinivas.kandagatla@linaro.org> wrote: >> Although we will not have more than one rpm in a system and therefore not >> instatiate this driver multiple times I do not want to run it off the >> global >> state. >> > I agree. > > Why not make a separate data structure for the qcom_of_data? > That might make sense, definitely worth a spin. >>> Should't you use the platform_get_resource_byname here? [...] > But my point on platform_get_resource_byname is to remove the dependency on > the resource ordering, It is very difficult to catch errors resulting in > wrong ordered resources in DT. > Sorry, forgot to answer the first part of your question. The order in dt must be fixed, hence there isn't really any reason to use the byname; as long as it doesn't add any context or such here. We had a long long discussion with the dt guys about this a few months back. >>>> + ret = irq_set_irq_wake(irq_ack, 1); >>>> + if (ret) >>>> + dev_warn(&pdev->dev, "failed to mark ack irq as >>>> wakeup\n"); >>>> + >>> >>> >>> ..] >>> >>> Shouln't these be set as part of the pm suspend call, if the device is >>> wakeup capable? >>> >>> >> >> Is there any reason to toggle this? >> >> I'm not sure when this interrupt will actually be fired, but I don't see >> any >> harm in keeping it wakup enabled at all times. > > > Typically the wake-up source is driven/enabled by the user. When the system > goes to low-power state it would enable the wakeup on the irq. And when > there is an interrupt it would wake up the system as part of resuming from > low-power state. > But the RPM is the user; the ack irq is used to acknowledge requests or to notify the Linux system about some event. If these happens when the system is asleep it's always expected (in current implementation at least) to wake up the system; hence I mark it as a wake irq. > Again if you what this interrupt to wakeup the system, I would expect > pm_wakeup_event/related calls in the interrupt handler too. > There is no power management here. I'll have to look into what value this would add. I have to get back to you on this. Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3383412..e5122a7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -482,6 +482,21 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config MFD_QCOM_RPM + tristate "Qualcomm Resource Power Manager (RPM)" + depends on ARCH_QCOM && OF + select MFD_CORE + help + If you say yes to this option, support will be included for the + Resource Power Manager system found in the Qualcomm 8660, 8960 and + 8064 based devices. + + This is required to access many regulators, clocks and bus + frequencies controlled by the RPM on these devices. + + Say M here if you want to include support for the Qualcomm RPM as a + module. This will build a module called "qcom_rpm". + config MFD_PM8XXX tristate diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2851275..bbe4209 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -150,6 +150,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o +obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c new file mode 100644 index 0000000..96135ab --- /dev/null +++ b/drivers/mfd/qcom_rpm.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2014, Sony Mobile Communications AB. + * Copyright (c) 2013, 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 <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <linux/interrupt.h> + +#include <linux/mfd/qcom_rpm.h> + +#include <dt-bindings/mfd/qcom_rpm.h> + +struct qcom_rpm_resource { + unsigned target_id; + unsigned status_id; + unsigned select_id; + unsigned size; +}; + +struct qcom_rpm { + struct device *dev; + struct completion ack; + struct mutex lock; + + void __iomem *status_regs; + void __iomem *ctrl_regs; + void __iomem *req_regs; + + void __iomem *ipc_rpm_reg; + + u32 ack_status; + + u32 version; + + const struct qcom_rpm_resource *resource_table; + unsigned n_resources; +}; + +#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) +#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) +#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) + +#define RPM_REQUEST_TIMEOUT (5 * HZ) + +#define RPM_REQUEST_CONTEXT 3 +#define RPM_REQ_SELECT 11 +#define RPM_ACK_CONTEXT 15 +#define RPM_ACK_SELECTOR 23 +#define RPM_SELECT_SIZE 7 + +#define RPM_ACTIVE_STATE BIT(0) +#define RPM_NOTIFICATION BIT(30) +#define RPM_REJECTED BIT(31) + +#define RPM_SIGNAL BIT(2) + +static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 }, + [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 }, + [QCOM_RPM_PM8921_S1] = { 116, 31, 30, 2 }, + [QCOM_RPM_PM8921_S2] = { 118, 33, 31, 2 }, + [QCOM_RPM_PM8921_S3] = { 120, 35, 32, 2 }, + [QCOM_RPM_PM8921_S4] = { 122, 37, 33, 2 }, + [QCOM_RPM_PM8921_S5] = { 124, 39, 34, 2 }, + [QCOM_RPM_PM8921_S6] = { 126, 41, 35, 2 }, + [QCOM_RPM_PM8921_S7] = { 128, 43, 36, 2 }, + [QCOM_RPM_PM8921_S8] = { 130, 45, 37, 2 }, + [QCOM_RPM_PM8921_L1] = { 132, 47, 38, 2 }, + [QCOM_RPM_PM8921_L2] = { 134, 49, 39, 2 }, + [QCOM_RPM_PM8921_L3] = { 136, 51, 40, 2 }, + [QCOM_RPM_PM8921_L4] = { 138, 53, 41, 2 }, + [QCOM_RPM_PM8921_L5] = { 140, 55, 42, 2 }, + [QCOM_RPM_PM8921_L6] = { 142, 57, 43, 2 }, + [QCOM_RPM_PM8921_L7] = { 144, 59, 44, 2 }, + [QCOM_RPM_PM8921_L8] = { 146, 61, 45, 2 }, + [QCOM_RPM_PM8921_L9] = { 148, 63, 46, 2 }, + [QCOM_RPM_PM8921_L10] = { 150, 65, 47, 2 }, + [QCOM_RPM_PM8921_L11] = { 152, 67, 48, 2 }, + [QCOM_RPM_PM8921_L12] = { 154, 69, 49, 2 }, + [QCOM_RPM_PM8921_L13] = { 156, 71, 50, 2 }, + [QCOM_RPM_PM8921_L14] = { 158, 73, 51, 2 }, + [QCOM_RPM_PM8921_L15] = { 160, 75, 52, 2 }, + [QCOM_RPM_PM8921_L16] = { 162, 77, 53, 2 }, + [QCOM_RPM_PM8921_L17] = { 164, 79, 54, 2 }, + [QCOM_RPM_PM8921_L18] = { 166, 81, 55, 2 }, + [QCOM_RPM_PM8921_L19] = { 168, 83, 56, 2 }, + [QCOM_RPM_PM8921_L20] = { 170, 85, 57, 2 }, + [QCOM_RPM_PM8921_L21] = { 172, 87, 58, 2 }, + [QCOM_RPM_PM8921_L22] = { 174, 89, 59, 2 }, + [QCOM_RPM_PM8921_L23] = { 176, 91, 60, 2 }, + [QCOM_RPM_PM8921_L24] = { 178, 93, 61, 2 }, + [QCOM_RPM_PM8921_L25] = { 180, 95, 62, 2 }, + [QCOM_RPM_PM8921_L26] = { 182, 97, 63, 2 }, + [QCOM_RPM_PM8921_L27] = { 184, 99, 64, 2 }, + [QCOM_RPM_PM8921_L28] = { 186, 101, 65, 2 }, + [QCOM_RPM_PM8921_L29] = { 188, 103, 66, 2 }, + [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 }, + [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 }, + [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 }, + [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 }, + [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 }, + [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 }, + [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 }, + [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 }, + [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 }, + [QCOM_RPM_PM8821_S1] = { 201, 116, 76, 2 }, + [QCOM_RPM_PM8821_S2] = { 203, 118, 77, 2 }, + [QCOM_RPM_PM8821_L1] = { 205, 120, 78, 2 }, + [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 }, + [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 }, + [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 }, +}; + +static const struct qcom_rpm apq8064_template = { + .version = 3, + .resource_table = apq8064_rpm_resource_table, + .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table), +}; + +static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 }, + [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 }, + [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 }, + [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 }, + [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 }, + [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 }, + [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 }, + [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 }, + [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 }, + [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 }, + [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 }, + [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 }, + [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 }, + [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 }, + [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 }, + [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 }, + [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 }, + [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 }, + [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 }, + [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 }, + [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 }, + [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 }, + [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 }, + [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 }, + [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 }, + [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 }, + [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 }, + [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 }, + [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 }, + [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 }, + [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 }, + [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 }, + [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 }, + [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 }, + [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 }, + [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 }, + [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 }, + [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 }, + [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 }, + [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 }, + [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 }, + [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 }, + [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 }, + [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 }, + [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 }, + [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 }, + [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 }, + [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 }, + [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 }, + [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 }, + [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 }, + [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 }, + [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 }, + [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 }, + [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 }, + [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 }, +}; + +static const struct qcom_rpm msm8660_template = { + .version = -1, + .resource_table = msm8660_rpm_resource_table, + .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table), +}; + +static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 }, + [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 }, + [QCOM_RPM_PM8921_S1] = { 117, 31, 30, 2 }, + [QCOM_RPM_PM8921_S2] = { 119, 33, 31, 2 }, + [QCOM_RPM_PM8921_S3] = { 121, 35, 32, 2 }, + [QCOM_RPM_PM8921_S4] = { 123, 37, 33, 2 }, + [QCOM_RPM_PM8921_S5] = { 125, 39, 34, 2 }, + [QCOM_RPM_PM8921_S6] = { 127, 41, 35, 2 }, + [QCOM_RPM_PM8921_S7] = { 129, 43, 36, 2 }, + [QCOM_RPM_PM8921_S8] = { 131, 45, 37, 2 }, + [QCOM_RPM_PM8921_L1] = { 133, 47, 38, 2 }, + [QCOM_RPM_PM8921_L2] = { 135, 49, 39, 2 }, + [QCOM_RPM_PM8921_L3] = { 137, 51, 40, 2 }, + [QCOM_RPM_PM8921_L4] = { 139, 53, 41, 2 }, + [QCOM_RPM_PM8921_L5] = { 141, 55, 42, 2 }, + [QCOM_RPM_PM8921_L6] = { 143, 57, 43, 2 }, + [QCOM_RPM_PM8921_L7] = { 145, 59, 44, 2 }, + [QCOM_RPM_PM8921_L8] = { 147, 61, 45, 2 }, + [QCOM_RPM_PM8921_L9] = { 149, 63, 46, 2 }, + [QCOM_RPM_PM8921_L10] = { 151, 65, 47, 2 }, + [QCOM_RPM_PM8921_L11] = { 153, 67, 48, 2 }, + [QCOM_RPM_PM8921_L12] = { 155, 69, 49, 2 }, + [QCOM_RPM_PM8921_L13] = { 157, 71, 50, 2 }, + [QCOM_RPM_PM8921_L14] = { 159, 73, 51, 2 }, + [QCOM_RPM_PM8921_L15] = { 161, 75, 52, 2 }, + [QCOM_RPM_PM8921_L16] = { 163, 77, 53, 2 }, + [QCOM_RPM_PM8921_L17] = { 165, 79, 54, 2 }, + [QCOM_RPM_PM8921_L18] = { 167, 81, 55, 2 }, + [QCOM_RPM_PM8921_L19] = { 169, 83, 56, 2 }, + [QCOM_RPM_PM8921_L20] = { 171, 85, 57, 2 }, + [QCOM_RPM_PM8921_L21] = { 173, 87, 58, 2 }, + [QCOM_RPM_PM8921_L22] = { 175, 89, 59, 2 }, + [QCOM_RPM_PM8921_L23] = { 177, 91, 60, 2 }, + [QCOM_RPM_PM8921_L24] = { 179, 93, 61, 2 }, + [QCOM_RPM_PM8921_L25] = { 181, 95, 62, 2 }, + [QCOM_RPM_PM8921_L26] = { 183, 97, 63, 2 }, + [QCOM_RPM_PM8921_L27] = { 185, 99, 64, 2 }, + [QCOM_RPM_PM8921_L28] = { 187, 101, 65, 2 }, + [QCOM_RPM_PM8921_L29] = { 189, 103, 66, 2 }, + [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 }, + [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 }, + [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 }, + [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 }, + [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 }, + [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 }, + [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 }, + [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 }, + [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 }, + [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 }, + [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 }, +}; + +static const struct qcom_rpm msm8960_template = { + .version = 3, + .resource_table = msm8960_rpm_resource_table, + .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), +}; + +static const struct of_device_id qcom_rpm_of_match[] = { + { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, + { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, + { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); + +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev) +{ + return dev_get_drvdata(dev); +} +EXPORT_SYMBOL(dev_get_qcom_rpm); + +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count) +{ + const struct qcom_rpm_resource *res; + u32 sel_mask[RPM_SELECT_SIZE] = { 0 }; + int left; + int ret = 0; + int i; + + if (WARN_ON(resource < 0 || resource >= rpm->n_resources)) + return -EINVAL; + + res = &rpm->resource_table[resource]; + if (WARN_ON(res->size != count)) + return -EINVAL; + + mutex_lock(&rpm->lock); + + for (i = 0; i < res->size; i++) + writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i)); + + bitmap_set((unsigned long *)sel_mask, res->select_id, 1); + for (i = 0; i < ARRAY_SIZE(sel_mask); i++) { + writel_relaxed(sel_mask[i], + RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i)); + } + + writel_relaxed(RPM_ACTIVE_STATE, + RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT)); + + reinit_completion(&rpm->ack); + writel(RPM_SIGNAL, rpm->ipc_rpm_reg); + + left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); + if (!left) + ret = -ETIMEDOUT; + else if (rpm->ack_status & RPM_REJECTED) + ret = -EIO; + + mutex_unlock(&rpm->lock); + + return ret; +} +EXPORT_SYMBOL(qcom_rpm_write); + +static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev) +{ + struct qcom_rpm *rpm = dev; + u32 ack; + int i; + + ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); + for (i = 0; i < RPM_SELECT_SIZE; i++) + writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i)); + writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); + + if (ack & RPM_NOTIFICATION) { + dev_warn(rpm->dev, "ignoring notification!\n"); + } else { + rpm->ack_status = ack; + complete(&rpm->ack); + } + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev) +{ + struct qcom_rpm *rpm = dev; + + writel(0x1, rpm->ipc_rpm_reg); + + dev_err(rpm->dev, "RPM triggered fatal error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +static int qcom_rpm_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct qcom_rpm *template; + struct resource *res; + struct qcom_rpm *rpm; + u32 fw_version[3]; + int irq_wakeup; + int irq_ack; + int irq_err; + int ret; + + rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); + if (!rpm) { + dev_err(&pdev->dev, "Can't allocate qcom_rpm\n"); + return -ENOMEM; + } + rpm->dev = &pdev->dev; + mutex_init(&rpm->lock); + init_completion(&rpm->ack); + + irq_ack = platform_get_irq_byname(pdev, "ack"); + if (irq_ack < 0) { + dev_err(&pdev->dev, "required ack interrupt missing\n"); + return irq_ack; + } + + irq_err = platform_get_irq_byname(pdev, "err"); + if (irq_err < 0) { + dev_err(&pdev->dev, "required err interrupt missing\n"); + return irq_err; + } + + irq_wakeup = platform_get_irq_byname(pdev, "wakeup"); + if (irq_wakeup < 0) { + dev_err(&pdev->dev, "required wakeup interrupt missing\n"); + return irq_wakeup; + } + + match = of_match_device(qcom_rpm_of_match, &pdev->dev); + template = match->data; + + rpm->version = template->version; + rpm->resource_table = template->resource_table; + rpm->n_resources = template->n_resources; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); + rpm->ctrl_regs = rpm->status_regs + 0x400; + rpm->req_regs = rpm->status_regs + 0x600; + if (IS_ERR(rpm->status_regs)) + return PTR_ERR(rpm->status_regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + rpm->ipc_rpm_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rpm->ipc_rpm_reg)) + return PTR_ERR(rpm->ipc_rpm_reg); + + dev_set_drvdata(&pdev->dev, rpm); + + fw_version[0] = readl(RPM_STATUS_REG(rpm, 0)); + fw_version[1] = readl(RPM_STATUS_REG(rpm, 1)); + fw_version[2] = readl(RPM_STATUS_REG(rpm, 2)); + if (fw_version[0] != rpm->version) { + dev_err(&pdev->dev, + "RPM version %u.%u.%u incompatible with driver version %u", + fw_version[0], + fw_version[1], + fw_version[2], + rpm->version); + return -EFAULT; + } + + dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0], + fw_version[1], + fw_version[2]); + + writel(fw_version[0], RPM_CTRL_REG(rpm, 0)); + writel(fw_version[1], RPM_CTRL_REG(rpm, 1)); + writel(fw_version[2], RPM_CTRL_REG(rpm, 2)); + + ret = devm_request_irq(&pdev->dev, + irq_ack, + qcom_rpm_ack_interrupt, + IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, + "qcom_rpm_ack", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request ack interrupt\n"); + return ret; + } + + ret = irq_set_irq_wake(irq_ack, 1); + if (ret) + dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n"); + + ret = devm_request_irq(&pdev->dev, + irq_err, + qcom_rpm_err_interrupt, + IRQF_TRIGGER_RISING, + "qcom_rpm_err", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request err interrupt\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, + irq_wakeup, + qcom_rpm_wakeup_interrupt, + IRQF_TRIGGER_RISING, + "qcom_rpm_wakeup", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request wakeup interrupt\n"); + return ret; + } + + ret = irq_set_irq_wake(irq_wakeup, 1); + if (ret) + dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n"); + + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); +} + +static int qcom_rpm_remove_child(struct device *dev, void *unused) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int qcom_rpm_remove(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, qcom_rpm_remove_child); + return 0; +} + +static struct platform_driver qcom_rpm_driver = { + .probe = qcom_rpm_probe, + .remove = qcom_rpm_remove, + .driver = { + .name = "qcom_rpm", + .owner = THIS_MODULE, + .of_match_table = qcom_rpm_of_match, + }, +}; + +static int __init qcom_rpm_init(void) +{ + return platform_driver_register(&qcom_rpm_driver); +} +arch_initcall(qcom_rpm_init); + +static void __exit qcom_rpm_exit(void) +{ + platform_driver_unregister(&qcom_rpm_driver); +} +module_exit(qcom_rpm_exit) + +MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h new file mode 100644 index 0000000..a52bc37 --- /dev/null +++ b/include/linux/mfd/qcom_rpm.h @@ -0,0 +1,12 @@ +#ifndef __QCOM_RPM_H__ +#define __QCOM_RPM_H__ + +#include <linux/types.h> + +struct device; +struct qcom_rpm; + +struct qcom_rpm *dev_get_qcom_rpm(struct device *dev); +int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count); + +#endif
Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960 and 8064 based devices. The driver exposes resources that child drivers can operate on; to implementing regulator, clock and bus frequency drivers. Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com> --- drivers/mfd/Kconfig | 15 ++ drivers/mfd/Makefile | 1 + drivers/mfd/qcom_rpm.c | 575 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/qcom_rpm.h | 12 + 4 files changed, 603 insertions(+) create mode 100644 drivers/mfd/qcom_rpm.c create mode 100644 include/linux/mfd/qcom_rpm.h