blob: 3267e50a313215e7b4aa145dc841e2c8589e0c55 [file] [log] [blame]
Simon Glass2ee1f6a2019-12-06 21:41:55 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Uclass for Primary-to-sideband bus, used to access various peripherals
4 *
5 * Copyright 2019 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#include <common.h>
10#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060011#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070012#include <malloc.h>
Simon Glass2ee1f6a2019-12-06 21:41:55 -070013#include <mapmem.h>
14#include <p2sb.h>
15#include <spl.h>
16#include <asm/io.h>
17#include <dm/uclass-internal.h>
18
19#define PCR_COMMON_IOSF_1_0 1
20
Simon Glass5e13b0f2020-07-07 21:32:30 -060021int p2sb_set_hide(struct udevice *dev, bool hide)
22{
23 struct p2sb_ops *ops = p2sb_get_ops(dev);
24
25 if (!ops->set_hide)
26 return -ENOSYS;
27
28 return ops->set_hide(dev, hide);
29}
30
Simon Glass25f16c12020-07-07 21:32:19 -060031void *pcr_reg_address(struct udevice *dev, uint offset)
Simon Glass2ee1f6a2019-12-06 21:41:55 -070032{
Simon Glass71fa5b42020-12-03 16:55:18 -070033 struct p2sb_child_platdata *pplat = dev_get_parent_plat(dev);
Simon Glass2ee1f6a2019-12-06 21:41:55 -070034 struct udevice *p2sb = dev_get_parent(dev);
35 struct p2sb_uc_priv *upriv = dev_get_uclass_priv(p2sb);
36 uintptr_t reg_addr;
37
38 /* Create an address based off of port id and offset */
39 reg_addr = upriv->mmio_base;
40 reg_addr += pplat->pid << PCR_PORTID_SHIFT;
41 reg_addr += offset;
42
43 return map_sysmem(reg_addr, 4);
44}
45
46/*
47 * The mapping of addresses via the SBREG_BAR assumes the IOSF-SB
48 * agents are using 32-bit aligned accesses for their configuration
49 * registers. For IOSF versions greater than 1_0, IOSF-SB
50 * agents can use any access (8/16/32 bit aligned) for their
51 * configuration registers
52 */
53static inline void check_pcr_offset_align(uint offset, uint size)
54{
55 const size_t align = PCR_COMMON_IOSF_1_0 ? sizeof(uint32_t) : size;
56
57 assert(IS_ALIGNED(offset, align));
58}
59
60uint pcr_read32(struct udevice *dev, uint offset)
61{
62 void *ptr;
63 uint val;
64
65 /* Ensure the PCR offset is correctly aligned */
66 assert(IS_ALIGNED(offset, sizeof(uint32_t)));
67
Simon Glass25f16c12020-07-07 21:32:19 -060068 ptr = pcr_reg_address(dev, offset);
Simon Glass2ee1f6a2019-12-06 21:41:55 -070069 val = readl(ptr);
70 unmap_sysmem(ptr);
71
72 return val;
73}
74
75uint pcr_read16(struct udevice *dev, uint offset)
76{
77 /* Ensure the PCR offset is correctly aligned */
78 check_pcr_offset_align(offset, sizeof(uint16_t));
79
Simon Glass25f16c12020-07-07 21:32:19 -060080 return readw(pcr_reg_address(dev, offset));
Simon Glass2ee1f6a2019-12-06 21:41:55 -070081}
82
83uint pcr_read8(struct udevice *dev, uint offset)
84{
85 /* Ensure the PCR offset is correctly aligned */
86 check_pcr_offset_align(offset, sizeof(uint8_t));
87
Simon Glass25f16c12020-07-07 21:32:19 -060088 return readb(pcr_reg_address(dev, offset));
Simon Glass2ee1f6a2019-12-06 21:41:55 -070089}
90
91/*
92 * After every write one needs to perform a read an innocuous register to
93 * ensure the writes are completed for certain ports. This is done for
94 * all ports so that the callers don't need the per-port knowledge for
95 * each transaction.
96 */
97static void write_completion(struct udevice *dev, uint offset)
98{
Simon Glass25f16c12020-07-07 21:32:19 -060099 readl(pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700100}
101
102void pcr_write32(struct udevice *dev, uint offset, uint indata)
103{
104 /* Ensure the PCR offset is correctly aligned */
105 assert(IS_ALIGNED(offset, sizeof(indata)));
106
Simon Glass25f16c12020-07-07 21:32:19 -0600107 writel(indata, pcr_reg_address(dev, offset));
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700108 /* Ensure the writes complete */
109 write_completion(dev, offset);
110}
111
112void pcr_write16(struct udevice *dev, uint offset, uint indata)
113{
114 /* Ensure the PCR offset is correctly aligned */
115 check_pcr_offset_align(offset, sizeof(uint16_t));
116
Simon Glass25f16c12020-07-07 21:32:19 -0600117 writew(indata, pcr_reg_address(dev, offset));
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700118 /* Ensure the writes complete */
119 write_completion(dev, offset);
120}
121
122void pcr_write8(struct udevice *dev, uint offset, uint indata)
123{
124 /* Ensure the PCR offset is correctly aligned */
125 check_pcr_offset_align(offset, sizeof(uint8_t));
126
Simon Glass25f16c12020-07-07 21:32:19 -0600127 writeb(indata, pcr_reg_address(dev, offset));
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700128 /* Ensure the writes complete */
129 write_completion(dev, offset);
130}
131
132void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set)
133{
134 uint data32;
135
136 data32 = pcr_read32(dev, offset);
137 data32 &= ~clr;
138 data32 |= set;
139 pcr_write32(dev, offset, data32);
140}
141
142void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set)
143{
144 uint data16;
145
146 data16 = pcr_read16(dev, offset);
147 data16 &= ~clr;
148 data16 |= set;
149 pcr_write16(dev, offset, data16);
150}
151
152void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set)
153{
154 uint data8;
155
156 data8 = pcr_read8(dev, offset);
157 data8 &= ~clr;
158 data8 |= set;
159 pcr_write8(dev, offset, data8);
160}
161
162int p2sb_get_port_id(struct udevice *dev)
163{
Simon Glass71fa5b42020-12-03 16:55:18 -0700164 struct p2sb_child_platdata *pplat = dev_get_parent_plat(dev);
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700165
166 return pplat->pid;
167}
168
169int p2sb_set_port_id(struct udevice *dev, int portid)
170{
171 struct udevice *ps2b;
172 struct p2sb_child_platdata *pplat;
173
174 if (!CONFIG_IS_ENABLED(OF_PLATDATA))
175 return -ENOSYS;
176
Simon Glass8d5bcef2020-10-03 11:31:39 -0600177 if (!CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)) {
178 uclass_find_first_device(UCLASS_P2SB, &ps2b);
179 if (!ps2b)
180 return -EDEADLK;
181 dev->parent = ps2b;
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700182
Simon Glass8d5bcef2020-10-03 11:31:39 -0600183 /*
184 * We must allocate this, since when the device was bound it did
185 * not have a parent.
186 */
Simon Glass71fa5b42020-12-03 16:55:18 -0700187 dev->parent_plat = malloc(sizeof(*pplat));
188 if (!dev->parent_plat)
Simon Glass8d5bcef2020-10-03 11:31:39 -0600189 return -ENOMEM;
190 }
Simon Glass71fa5b42020-12-03 16:55:18 -0700191 pplat = dev_get_parent_plat(dev);
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700192 pplat->pid = portid;
193
194 return 0;
195}
196
197static int p2sb_child_post_bind(struct udevice *dev)
198{
199#if !CONFIG_IS_ENABLED(OF_PLATDATA)
Simon Glass71fa5b42020-12-03 16:55:18 -0700200 struct p2sb_child_platdata *pplat = dev_get_parent_plat(dev);
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700201 int ret;
202 u32 pid;
203
204 ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid);
205 if (ret)
206 return ret;
207 pplat->pid = pid;
208#endif
209
210 return 0;
211}
212
213static int p2sb_post_bind(struct udevice *dev)
214{
215 if (spl_phase() > PHASE_TPL && !CONFIG_IS_ENABLED(OF_PLATDATA))
216 return dm_scan_fdt_dev(dev);
217
218 return 0;
219}
220
221UCLASS_DRIVER(p2sb) = {
222 .id = UCLASS_P2SB,
223 .name = "p2sb",
Simon Glass8a2b47f2020-12-03 16:55:17 -0700224 .per_device_auto = sizeof(struct p2sb_uc_priv),
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700225 .post_bind = p2sb_post_bind,
226 .child_post_bind = p2sb_child_post_bind,
Simon Glass33b2efb2020-12-03 16:55:22 -0700227 .per_child_plat_auto = sizeof(struct p2sb_child_platdata),
Simon Glass2ee1f6a2019-12-06 21:41:55 -0700228};