]>
Commit | Line | Data |
---|---|---|
5b47271c MK |
1 | /* |
2 | * Qualcomm SPMI bus driver | |
3 | * | |
4 | * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> | |
5 | * | |
6 | * Loosely based on Little Kernel driver | |
7 | * | |
8 | * SPDX-License-Identifier: BSD-3-Clause | |
9 | */ | |
10 | ||
11 | #include <common.h> | |
12 | #include <dm.h> | |
13 | #include <errno.h> | |
14 | #include <fdtdec.h> | |
15 | #include <asm/io.h> | |
16 | #include <spmi/spmi.h> | |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | ||
210d9592 JRO |
20 | /* PMIC Arbiter configuration registers */ |
21 | #define PMIC_ARB_VERSION 0x0000 | |
22 | #define PMIC_ARB_VERSION_V2_MIN 0x20010000 | |
23 | ||
5b47271c MK |
24 | #define ARB_CHANNEL_OFFSET(n) (0x4 * (n)) |
25 | #define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000) | |
26 | ||
27 | #define SPMI_REG_CMD0 0x0 | |
28 | #define SPMI_REG_CONFIG 0x4 | |
29 | #define SPMI_REG_STATUS 0x8 | |
30 | #define SPMI_REG_WDATA 0x10 | |
31 | #define SPMI_REG_RDATA 0x18 | |
32 | ||
33 | #define SPMI_CMD_OPCODE_SHIFT 27 | |
34 | #define SPMI_CMD_SLAVE_ID_SHIFT 20 | |
35 | #define SPMI_CMD_ADDR_SHIFT 12 | |
36 | #define SPMI_CMD_ADDR_OFFSET_SHIFT 4 | |
37 | #define SPMI_CMD_BYTE_CNT_SHIFT 0 | |
38 | ||
39 | #define SPMI_CMD_EXT_REG_WRITE_LONG 0x00 | |
40 | #define SPMI_CMD_EXT_REG_READ_LONG 0x01 | |
41 | ||
42 | #define SPMI_STATUS_DONE 0x1 | |
43 | ||
44 | #define SPMI_MAX_CHANNELS 128 | |
45 | #define SPMI_MAX_SLAVES 16 | |
46 | #define SPMI_MAX_PERIPH 256 | |
47 | ||
48 | struct msm_spmi_priv { | |
49 | phys_addr_t arb_chnl; /* ARB channel mapping base */ | |
50 | phys_addr_t spmi_core; /* SPMI core */ | |
51 | phys_addr_t spmi_obs; /* SPMI observer */ | |
52 | /* SPMI channel map */ | |
53 | uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH]; | |
54 | }; | |
55 | ||
56 | static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, | |
57 | uint8_t val) | |
58 | { | |
59 | struct msm_spmi_priv *priv = dev_get_priv(dev); | |
60 | unsigned channel; | |
61 | uint32_t reg = 0; | |
62 | ||
63 | if (usid >= SPMI_MAX_SLAVES) | |
64 | return -EIO; | |
65 | if (pid >= SPMI_MAX_PERIPH) | |
66 | return -EIO; | |
67 | ||
68 | channel = priv->channel_map[usid][pid]; | |
69 | ||
70 | /* Disable IRQ mode for the current channel*/ | |
71 | writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) + | |
72 | SPMI_REG_CONFIG); | |
73 | ||
74 | /* Write single byte */ | |
75 | writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA); | |
76 | ||
77 | /* Prepare write command */ | |
78 | reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT; | |
79 | reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT); | |
80 | reg |= (pid << SPMI_CMD_ADDR_SHIFT); | |
81 | reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT); | |
82 | reg |= 1; /* byte count */ | |
83 | ||
84 | /* Send write command */ | |
85 | writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); | |
86 | ||
87 | /* Wait till CMD DONE status */ | |
88 | reg = 0; | |
89 | while (!reg) { | |
90 | reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) + | |
91 | SPMI_REG_STATUS); | |
92 | } | |
93 | ||
94 | if (reg ^ SPMI_STATUS_DONE) { | |
95 | printf("SPMI write failure.\n"); | |
96 | return -EIO; | |
97 | } | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) | |
103 | { | |
104 | struct msm_spmi_priv *priv = dev_get_priv(dev); | |
105 | unsigned channel; | |
106 | uint32_t reg = 0; | |
107 | ||
108 | if (usid >= SPMI_MAX_SLAVES) | |
109 | return -EIO; | |
110 | if (pid >= SPMI_MAX_PERIPH) | |
111 | return -EIO; | |
112 | ||
113 | channel = priv->channel_map[usid][pid]; | |
114 | ||
115 | /* Disable IRQ mode for the current channel*/ | |
116 | writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG); | |
117 | ||
118 | /* Prepare read command */ | |
119 | reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT; | |
120 | reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT); | |
121 | reg |= (pid << SPMI_CMD_ADDR_SHIFT); | |
122 | reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT); | |
123 | reg |= 1; /* byte count */ | |
124 | ||
125 | /* Request read */ | |
126 | writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); | |
127 | ||
128 | /* Wait till CMD DONE status */ | |
129 | reg = 0; | |
130 | while (!reg) { | |
131 | reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + | |
132 | SPMI_REG_STATUS); | |
133 | } | |
134 | ||
135 | if (reg ^ SPMI_STATUS_DONE) { | |
136 | printf("SPMI read failure.\n"); | |
137 | return -EIO; | |
138 | } | |
139 | ||
140 | /* Read the data */ | |
141 | return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + | |
142 | SPMI_REG_RDATA) & 0xFF; | |
143 | } | |
144 | ||
145 | static struct dm_spmi_ops msm_spmi_ops = { | |
146 | .read = msm_spmi_read, | |
147 | .write = msm_spmi_write, | |
148 | }; | |
149 | ||
150 | static int msm_spmi_probe(struct udevice *dev) | |
151 | { | |
152 | struct udevice *parent = dev->parent; | |
153 | struct msm_spmi_priv *priv = dev_get_priv(dev); | |
e160f7d4 | 154 | int node = dev_of_offset(dev); |
210d9592 JRO |
155 | u32 hw_ver; |
156 | bool is_v1; | |
5b47271c MK |
157 | int i; |
158 | ||
a821c4af | 159 | priv->arb_chnl = devfdt_get_addr(dev); |
5b47271c | 160 | priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, |
e160f7d4 | 161 | dev_of_offset(parent), node, "reg", 1, NULL, false); |
5b47271c | 162 | priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, |
e160f7d4 | 163 | dev_of_offset(parent), node, "reg", 2, NULL, false); |
210d9592 JRO |
164 | |
165 | hw_ver = readl(priv->arb_chnl + PMIC_ARB_VERSION - 0x800); | |
166 | is_v1 = (hw_ver < PMIC_ARB_VERSION_V2_MIN); | |
167 | ||
168 | dev_dbg(dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2), hw_ver); | |
169 | ||
5b47271c MK |
170 | if (priv->arb_chnl == FDT_ADDR_T_NONE || |
171 | priv->spmi_core == FDT_ADDR_T_NONE || | |
172 | priv->spmi_obs == FDT_ADDR_T_NONE) | |
173 | return -EINVAL; | |
174 | ||
175 | /* Scan peripherals connected to each SPMI channel */ | |
bd3006c8 | 176 | for (i = 0; i < SPMI_MAX_PERIPH ; i++) { |
5b47271c MK |
177 | uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i)); |
178 | uint8_t slave_id = (periph & 0xf0000) >> 16; | |
179 | uint8_t pid = (periph & 0xff00) >> 8; | |
180 | ||
181 | priv->channel_map[slave_id][pid] = i; | |
182 | } | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static const struct udevice_id msm_spmi_ids[] = { | |
187 | { .compatible = "qcom,spmi-pmic-arb" }, | |
188 | { } | |
189 | }; | |
190 | ||
191 | U_BOOT_DRIVER(msm_spmi) = { | |
192 | .name = "msm_spmi", | |
193 | .id = UCLASS_SPMI, | |
194 | .of_match = msm_spmi_ids, | |
195 | .ops = &msm_spmi_ops, | |
196 | .probe = msm_spmi_probe, | |
197 | .priv_auto_alloc_size = sizeof(struct msm_spmi_priv), | |
198 | }; |