blob: 06b1e8d9ad7cf0643234be24b8971843f32c4fea [file] [log] [blame]
Simon Glass5bee27a2019-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 Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.h>
Simon Glass336d4612020-02-03 07:36:16 -070012#include <malloc.h>
Simon Glass5bee27a2019-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
21static void *_pcr_reg_address(struct udevice *dev, uint offset)
22{
23 struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
24 struct udevice *p2sb = dev_get_parent(dev);
25 struct p2sb_uc_priv *upriv = dev_get_uclass_priv(p2sb);
26 uintptr_t reg_addr;
27
28 /* Create an address based off of port id and offset */
29 reg_addr = upriv->mmio_base;
30 reg_addr += pplat->pid << PCR_PORTID_SHIFT;
31 reg_addr += offset;
32
33 return map_sysmem(reg_addr, 4);
34}
35
36/*
37 * The mapping of addresses via the SBREG_BAR assumes the IOSF-SB
38 * agents are using 32-bit aligned accesses for their configuration
39 * registers. For IOSF versions greater than 1_0, IOSF-SB
40 * agents can use any access (8/16/32 bit aligned) for their
41 * configuration registers
42 */
43static inline void check_pcr_offset_align(uint offset, uint size)
44{
45 const size_t align = PCR_COMMON_IOSF_1_0 ? sizeof(uint32_t) : size;
46
47 assert(IS_ALIGNED(offset, align));
48}
49
50uint pcr_read32(struct udevice *dev, uint offset)
51{
52 void *ptr;
53 uint val;
54
55 /* Ensure the PCR offset is correctly aligned */
56 assert(IS_ALIGNED(offset, sizeof(uint32_t)));
57
58 ptr = _pcr_reg_address(dev, offset);
59 val = readl(ptr);
60 unmap_sysmem(ptr);
61
62 return val;
63}
64
65uint pcr_read16(struct udevice *dev, uint offset)
66{
67 /* Ensure the PCR offset is correctly aligned */
68 check_pcr_offset_align(offset, sizeof(uint16_t));
69
70 return readw(_pcr_reg_address(dev, offset));
71}
72
73uint pcr_read8(struct udevice *dev, uint offset)
74{
75 /* Ensure the PCR offset is correctly aligned */
76 check_pcr_offset_align(offset, sizeof(uint8_t));
77
78 return readb(_pcr_reg_address(dev, offset));
79}
80
81/*
82 * After every write one needs to perform a read an innocuous register to
83 * ensure the writes are completed for certain ports. This is done for
84 * all ports so that the callers don't need the per-port knowledge for
85 * each transaction.
86 */
87static void write_completion(struct udevice *dev, uint offset)
88{
89 readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
90}
91
92void pcr_write32(struct udevice *dev, uint offset, uint indata)
93{
94 /* Ensure the PCR offset is correctly aligned */
95 assert(IS_ALIGNED(offset, sizeof(indata)));
96
97 writel(indata, _pcr_reg_address(dev, offset));
98 /* Ensure the writes complete */
99 write_completion(dev, offset);
100}
101
102void pcr_write16(struct udevice *dev, uint offset, uint indata)
103{
104 /* Ensure the PCR offset is correctly aligned */
105 check_pcr_offset_align(offset, sizeof(uint16_t));
106
107 writew(indata, _pcr_reg_address(dev, offset));
108 /* Ensure the writes complete */
109 write_completion(dev, offset);
110}
111
112void pcr_write8(struct udevice *dev, uint offset, uint indata)
113{
114 /* Ensure the PCR offset is correctly aligned */
115 check_pcr_offset_align(offset, sizeof(uint8_t));
116
117 writeb(indata, _pcr_reg_address(dev, offset));
118 /* Ensure the writes complete */
119 write_completion(dev, offset);
120}
121
122void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set)
123{
124 uint data32;
125
126 data32 = pcr_read32(dev, offset);
127 data32 &= ~clr;
128 data32 |= set;
129 pcr_write32(dev, offset, data32);
130}
131
132void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set)
133{
134 uint data16;
135
136 data16 = pcr_read16(dev, offset);
137 data16 &= ~clr;
138 data16 |= set;
139 pcr_write16(dev, offset, data16);
140}
141
142void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set)
143{
144 uint data8;
145
146 data8 = pcr_read8(dev, offset);
147 data8 &= ~clr;
148 data8 |= set;
149 pcr_write8(dev, offset, data8);
150}
151
152int p2sb_get_port_id(struct udevice *dev)
153{
154 struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
155
156 return pplat->pid;
157}
158
159int p2sb_set_port_id(struct udevice *dev, int portid)
160{
161 struct udevice *ps2b;
162 struct p2sb_child_platdata *pplat;
163
164 if (!CONFIG_IS_ENABLED(OF_PLATDATA))
165 return -ENOSYS;
166
167 uclass_find_first_device(UCLASS_P2SB, &ps2b);
168 if (!ps2b)
169 return -EDEADLK;
170 dev->parent = ps2b;
171
172 /*
173 * We must allocate this, since when the device was bound it did not
174 * have a parent.
175 * TODO(sjg@chromium.org): Add a parent pointer to child devices in dtoc
176 */
177 dev->parent_platdata = malloc(sizeof(*pplat));
178 if (!dev->parent_platdata)
179 return -ENOMEM;
180 pplat = dev_get_parent_platdata(dev);
181 pplat->pid = portid;
182
183 return 0;
184}
185
186static int p2sb_child_post_bind(struct udevice *dev)
187{
188#if !CONFIG_IS_ENABLED(OF_PLATDATA)
189 struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
190 int ret;
191 u32 pid;
192
193 ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid);
194 if (ret)
195 return ret;
196 pplat->pid = pid;
197#endif
198
199 return 0;
200}
201
202static int p2sb_post_bind(struct udevice *dev)
203{
204 if (spl_phase() > PHASE_TPL && !CONFIG_IS_ENABLED(OF_PLATDATA))
205 return dm_scan_fdt_dev(dev);
206
207 return 0;
208}
209
210UCLASS_DRIVER(p2sb) = {
211 .id = UCLASS_P2SB,
212 .name = "p2sb",
213 .per_device_auto_alloc_size = sizeof(struct p2sb_uc_priv),
214 .post_bind = p2sb_post_bind,
215 .child_post_bind = p2sb_child_post_bind,
216 .per_child_platdata_auto_alloc_size =
217 sizeof(struct p2sb_child_platdata),
218};