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