summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/mfd/timberdale.h
blob: 4412acd826fa3523fd0af43f4128b8d685939620 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * timberdale.h timberdale FPGA MFD driver defines
 * Copyright (c) 2009 Intel Corporation
 *
 * This program 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 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Supports:
 * Timberdale FPGA
 */

#ifndef MFD_TIMBERDALE_H
#define MFD_TIMBERDALE_H

#define DRV_VERSION		"0.3"

/* This driver only support versions >= 3.8 and < 4.0  */
#define TIMB_SUPPORTED_MAJOR	3

/* This driver only support minor >= 8 */
#define TIMB_REQUIRED_MINOR	8

/* Registers of the control area */
#define TIMB_REV_MAJOR	0x00
#define TIMB_REV_MINOR	0x04
#define TIMB_HW_CONFIG	0x08
#define TIMB_SW_RST	0x40

/* bits in the TIMB_HW_CONFIG register */
#define TIMB_HW_CONFIG_SPI_8BIT	0x80

#define TIMB_HW_VER_MASK	0x0f
#define TIMB_HW_VER0		0x00
#define TIMB_HW_VER1		0x01
#define TIMB_HW_VER2		0x02
#define TIMB_HW_VER3		0x03

#define OCORESOFFSET	0x0
#define OCORESEND	0x1f

#define SPIOFFSET	0x80
#define SPIEND		0xff

#define UARTLITEOFFSET	0x100
#define UARTLITEEND	0x10f

#define RDSOFFSET	0x180
#define RDSEND		0x183

#define ETHOFFSET	0x300
#define ETHEND		0x3ff

#define GPIOOFFSET	0x400
#define GPIOEND		0x7ff

#define CHIPCTLOFFSET	0x800
#define CHIPCTLEND	0x8ff
#define CHIPCTLSIZE	(CHIPCTLEND - CHIPCTLOFFSET + 1)

#define INTCOFFSET	0xc00
#define INTCEND		0xfff
#define INTCSIZE	(INTCEND - INTCOFFSET)

#define MOSTOFFSET	0x1000
#define MOSTEND		0x13ff

#define UARTOFFSET	0x1400
#define UARTEND		0x17ff

#define XIICOFFSET	0x1800
#define XIICEND		0x19ff

#define I2SOFFSET	0x1C00
#define I2SEND		0x1fff

#define LOGIWOFFSET	0x30000
#define LOGIWEND	0x37fff

#define MLCOREOFFSET	0x40000
#define MLCOREEND	0x43fff

#define DMAOFFSET	0x01000000
#define DMAEND		0x013fffff

/* SDHC0 is placed in PCI bar 1 */
#define SDHC0OFFSET	0x00
#define SDHC0END	0xff

/* SDHC1 is placed in PCI bar 2 */
#define SDHC1OFFSET	0x00
#define SDHC1END	0xff

#define PCI_VENDOR_ID_TIMB	0x10ee
#define PCI_DEVICE_ID_TIMB	0xa123

#define IRQ_TIMBERDALE_INIC		0
#define IRQ_TIMBERDALE_MLB		1
#define IRQ_TIMBERDALE_GPIO		2
#define IRQ_TIMBERDALE_I2C		3
#define IRQ_TIMBERDALE_UART		4
#define IRQ_TIMBERDALE_DMA		5
#define IRQ_TIMBERDALE_I2S		6
#define IRQ_TIMBERDALE_TSC_INT		7
#define IRQ_TIMBERDALE_SDHC		8
#define IRQ_TIMBERDALE_ADV7180		9
#define IRQ_TIMBERDALE_ETHSW_IF		10
#define IRQ_TIMBERDALE_SPI		11
#define IRQ_TIMBERDALE_UARTLITE		12
#define IRQ_TIMBERDALE_MLCORE		13
#define IRQ_TIMBERDALE_MLCORE_BUF	14
#define IRQ_TIMBERDALE_RDS		15
#define TIMBERDALE_NR_IRQS		16

#define GPIO_PIN_ASCB		8
#define GPIO_PIN_INIC_RST	14
#define GPIO_PIN_BT_RST		15
#define GPIO_NR_PINS		16

/* DMA Channels */
#define DMA_UART_RX         0
#define DMA_UART_TX         1
#define DMA_MLB_RX          2
#define DMA_MLB_TX          3
#define DMA_VIDEO_RX        4
#define DMA_VIDEO_DROP      5
#define DMA_SDHCI_RX        6
#define DMA_SDHCI_TX        7
#define DMA_ETH_RX          8
#define DMA_ETH_TX          9

#endif
d='n505' href='#n505'>505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/*
 * Copyright (c) 2014, Sony Mobile Communications AB.
 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
 * Author: Bjorn Andersson <bjorn.andersson@sonymobile.com>
 *
 * 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 <linux/mfd/syscon.h>
#include <linux/regmap.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_data {
	u32 version;
	const struct qcom_rpm_resource *resource_table;
	unsigned n_resources;
};

struct qcom_rpm {
	struct device *dev;
	struct regmap *ipc_regmap;
	unsigned ipc_offset;
	unsigned ipc_bit;

	struct completion ack;
	struct mutex lock;

	void __iomem *status_regs;
	void __iomem *ctrl_regs;
	void __iomem *req_regs;

	u32 ack_status;

	const struct qcom_rpm_data *data;
};

#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_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_SMPS1] =		{ 116, 31, 30, 2 },
	[QCOM_RPM_PM8921_SMPS2] =		{ 118, 33, 31, 2 },
	[QCOM_RPM_PM8921_SMPS3] =		{ 120, 35, 32, 2 },
	[QCOM_RPM_PM8921_SMPS4] =		{ 122, 37, 33, 2 },
	[QCOM_RPM_PM8921_SMPS5] =		{ 124, 39, 34, 2 },
	[QCOM_RPM_PM8921_SMPS6] =		{ 126, 41, 35, 2 },
	[QCOM_RPM_PM8921_SMPS7] =		{ 128, 43, 36, 2 },
	[QCOM_RPM_PM8921_SMPS8] =		{ 130, 45, 37, 2 },
	[QCOM_RPM_PM8921_LDO1] =		{ 132, 47, 38, 2 },
	[QCOM_RPM_PM8921_LDO2] =		{ 134, 49, 39, 2 },
	[QCOM_RPM_PM8921_LDO3] =		{ 136, 51, 40, 2 },
	[QCOM_RPM_PM8921_LDO4] =		{ 138, 53, 41, 2 },
	[QCOM_RPM_PM8921_LDO5] =		{ 140, 55, 42, 2 },
	[QCOM_RPM_PM8921_LDO6] =		{ 142, 57, 43, 2 },
	[QCOM_RPM_PM8921_LDO7] =		{ 144, 59, 44, 2 },
	[QCOM_RPM_PM8921_LDO8] =		{ 146, 61, 45, 2 },
	[QCOM_RPM_PM8921_LDO9] =		{ 148, 63, 46, 2 },
	[QCOM_RPM_PM8921_LDO10] =		{ 150, 65, 47, 2 },
	[QCOM_RPM_PM8921_LDO11] =		{ 152, 67, 48, 2 },
	[QCOM_RPM_PM8921_LDO12] =		{ 154, 69, 49, 2 },
	[QCOM_RPM_PM8921_LDO13] =		{ 156, 71, 50, 2 },
	[QCOM_RPM_PM8921_LDO14] =		{ 158, 73, 51, 2 },
	[QCOM_RPM_PM8921_LDO15] =		{ 160, 75, 52, 2 },
	[QCOM_RPM_PM8921_LDO16] =		{ 162, 77, 53, 2 },
	[QCOM_RPM_PM8921_LDO17] =		{ 164, 79, 54, 2 },
	[QCOM_RPM_PM8921_LDO18] =		{ 166, 81, 55, 2 },
	[QCOM_RPM_PM8921_LDO19] =		{ 168, 83, 56, 2 },
	[QCOM_RPM_PM8921_LDO20] =		{ 170, 85, 57, 2 },
	[QCOM_RPM_PM8921_LDO21] =		{ 172, 87, 58, 2 },
	[QCOM_RPM_PM8921_LDO22] =		{ 174, 89, 59, 2 },
	[QCOM_RPM_PM8921_LDO23] =		{ 176, 91, 60, 2 },
	[QCOM_RPM_PM8921_LDO24] =		{ 178, 93, 61, 2 },
	[QCOM_RPM_PM8921_LDO25] =		{ 180, 95, 62, 2 },
	[QCOM_RPM_PM8921_LDO26] =		{ 182, 97, 63, 2 },
	[QCOM_RPM_PM8921_LDO27] =		{ 184, 99, 64, 2 },
	[QCOM_RPM_PM8921_LDO28] =		{ 186, 101, 65, 2 },
	[QCOM_RPM_PM8921_LDO29] =		{ 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_SMPS1] =		{ 201, 116, 76, 2 },
	[QCOM_RPM_PM8821_SMPS2] =		{ 203, 118, 77, 2 },
	[QCOM_RPM_PM8821_LDO1] =		{ 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_QDSS_CLK] =			{ 214, ~0, 7, 1 },
	[QCOM_RPM_VDDMIN_GPIO] =		{ 215, 131, 89, 1 },
};

static const struct qcom_rpm_data 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_data msm8660_template = {
	.version = 2,
	.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_SMPS1] =		{ 117, 31, 30, 2 },
	[QCOM_RPM_PM8921_SMPS2] =		{ 119, 33, 31, 2 },
	[QCOM_RPM_PM8921_SMPS3] =		{ 121, 35, 32, 2 },
	[QCOM_RPM_PM8921_SMPS4] =		{ 123, 37, 33, 2 },
	[QCOM_RPM_PM8921_SMPS5] =		{ 125, 39, 34, 2 },
	[QCOM_RPM_PM8921_SMPS6] =		{ 127, 41, 35, 2 },
	[QCOM_RPM_PM8921_SMPS7] =		{ 129, 43, 36, 2 },
	[QCOM_RPM_PM8921_SMPS8] =		{ 131, 45, 37, 2 },
	[QCOM_RPM_PM8921_LDO1] =		{ 133, 47, 38, 2 },
	[QCOM_RPM_PM8921_LDO2] =		{ 135, 49, 39, 2 },
	[QCOM_RPM_PM8921_LDO3] =		{ 137, 51, 40, 2 },
	[QCOM_RPM_PM8921_LDO4] =		{ 139, 53, 41, 2 },
	[QCOM_RPM_PM8921_LDO5] =		{ 141, 55, 42, 2 },
	[QCOM_RPM_PM8921_LDO6] =		{ 143, 57, 43, 2 },
	[QCOM_RPM_PM8921_LDO7] =		{ 145, 59, 44, 2 },
	[QCOM_RPM_PM8921_LDO8] =		{ 147, 61, 45, 2 },
	[QCOM_RPM_PM8921_LDO9] =		{ 149, 63, 46, 2 },
	[QCOM_RPM_PM8921_LDO10] =		{ 151, 65, 47, 2 },
	[QCOM_RPM_PM8921_LDO11] =		{ 153, 67, 48, 2 },
	[QCOM_RPM_PM8921_LDO12] =		{ 155, 69, 49, 2 },
	[QCOM_RPM_PM8921_LDO13] =		{ 157, 71, 50, 2 },
	[QCOM_RPM_PM8921_LDO14] =		{ 159, 73, 51, 2 },
	[QCOM_RPM_PM8921_LDO15] =		{ 161, 75, 52, 2 },
	[QCOM_RPM_PM8921_LDO16] =		{ 163, 77, 53, 2 },
	[QCOM_RPM_PM8921_LDO17] =		{ 165, 79, 54, 2 },
	[QCOM_RPM_PM8921_LDO18] =		{ 167, 81, 55, 2 },
	[QCOM_RPM_PM8921_LDO19] =		{ 169, 83, 56, 2 },
	[QCOM_RPM_PM8921_LDO20] =		{ 171, 85, 57, 2 },
	[QCOM_RPM_PM8921_LDO21] =		{ 173, 87, 58, 2 },
	[QCOM_RPM_PM8921_LDO22] =		{ 175, 89, 59, 2 },
	[QCOM_RPM_PM8921_LDO23] =		{ 177, 91, 60, 2 },
	[QCOM_RPM_PM8921_LDO24] =		{ 179, 93, 61, 2 },
	[QCOM_RPM_PM8921_LDO25] =		{ 181, 95, 62, 2 },
	[QCOM_RPM_PM8921_LDO26] =		{ 183, 97, 63, 2 },
	[QCOM_RPM_PM8921_LDO27] =		{ 185, 99, 64, 2 },
	[QCOM_RPM_PM8921_LDO28] =		{ 187, 101, 65, 2 },
	[QCOM_RPM_PM8921_LDO29] =		{ 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_data msm8960_template = {
	.version = 3,
	.resource_table = msm8960_rpm_resource_table,
	.n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
};

static const struct qcom_rpm_resource ipq806x_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_NSS_FABRIC_0_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_NSS_FABRIC_1_CLK] =		{ 33, 17, 14, 1 },
	[QCOM_RPM_EBI1_CLK] =			{ 34, 18, 16, 1 },
	[QCOM_RPM_APPS_FABRIC_HALT] =		{ 35, 19, 18, 2 },
	[QCOM_RPM_APPS_FABRIC_MODE] =		{ 37, 20, 19, 3 },
	[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, 2 },
	[QCOM_RPM_SYS_FABRIC_MODE] =		{ 55, 24, 23, 3 },
	[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, 2 },
	[QCOM_RPM_MM_FABRIC_MODE] =		{ 91, 28, 27, 3 },
	[QCOM_RPM_MM_FABRIC_IOCTL] =		{ 94, 29, 28, 1 },
	[QCOM_RPM_MM_FABRIC_ARB] =		{ 95, 30, 29, 2 },
	[QCOM_RPM_CXO_BUFFERS] =		{ 209, 33, 31, 1 },
	[QCOM_RPM_USB_OTG_SWITCH] =		{ 210, 34, 32, 1 },
	[QCOM_RPM_HDMI_SWITCH] =		{ 211, 35, 33, 1 },
	[QCOM_RPM_DDR_DMM] =			{ 212, 36, 34, 2 },
	[QCOM_RPM_VDDMIN_GPIO] =		{ 215, 40, 39, 1 },
	[QCOM_RPM_SMB208_S1a] =			{ 216, 41, 90, 2 },
	[QCOM_RPM_SMB208_S1b] =			{ 218, 43, 91, 2 },
	[QCOM_RPM_SMB208_S2a] =			{ 220, 45, 92, 2 },
	[QCOM_RPM_SMB208_S2b] =			{ 222, 47, 93, 2 },
};

static const struct qcom_rpm_data ipq806x_template = {
	.version = 3,
	.resource_table = ipq806x_rpm_resource_table,
	.n_resources = ARRAY_SIZE(ipq806x_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 },
	{ .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
	{ }
};
MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);

int qcom_rpm_write(struct qcom_rpm *rpm,
		   int state,
		   int resource,
		   u32 *buf, size_t count)
{
	const struct qcom_rpm_resource *res;
	const struct qcom_rpm_data *data = rpm->data;
	u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
	int left;
	int ret = 0;
	int i;

	if (WARN_ON(resource < 0 || resource >= data->n_resources))
		return -EINVAL;

	res = &data->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(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));

	reinit_completion(&rpm->ack);
	regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));

	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;

	regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
	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;
	struct device_node *syscon_np;
	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)
		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);
	rpm->data = match->data;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(rpm->status_regs))
		return PTR_ERR(rpm->status_regs);
	rpm->ctrl_regs = rpm->status_regs + 0x400;
	rpm->req_regs = rpm->status_regs + 0x600;

	syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
	if (!syscon_np) {
		dev_err(&pdev->dev, "no qcom,ipc node\n");
		return -ENODEV;
	}

	rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
	if (IS_ERR(rpm->ipc_regmap))
		return PTR_ERR(rpm->ipc_regmap);

	ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
					 &rpm->ipc_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "no offset in qcom,ipc\n");
		return -EINVAL;
	}

	ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
					 &rpm->ipc_bit);
	if (ret < 0) {
		dev_err(&pdev->dev, "no bit in qcom,ipc\n");
		return -EINVAL;
	}

	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->data->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->data->version);
		return -EFAULT;
	}

	dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
							fw_version[1],
							fw_version[2]);

	ret = devm_request_irq(&pdev->dev,
			       irq_ack,
			       qcom_rpm_ack_interrupt,
			       IRQF_TRIGGER_RISING,
			       "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(struct platform_device *pdev)
{
	of_platform_depopulate(&pdev->dev);
	return 0;
}

static struct platform_driver qcom_rpm_driver = {
	.probe = qcom_rpm_probe,
	.remove = qcom_rpm_remove,
	.driver  = {
		.name  = "qcom_rpm",
		.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");
MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");