1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs *
4c39f472eSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs *
11c39f472eSBen Skeggs * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs *
14c39f472eSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17c39f472eSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs *
22c39f472eSBen Skeggs * Authors: Ben Skeggs
23c39f472eSBen Skeggs */
24c39f472eSBen Skeggs #include "mxms.h"
25c39f472eSBen Skeggs
26*9936aeeaSIlia Mirkin #define ROM16(x) get_unaligned_le16(&(x))
27*9936aeeaSIlia Mirkin #define ROM32(x) get_unaligned_le32(&(x))
28c39f472eSBen Skeggs
29c39f472eSBen Skeggs static u8 *
mxms_data(struct nvkm_mxm * mxm)30be83cd4eSBen Skeggs mxms_data(struct nvkm_mxm *mxm)
31c39f472eSBen Skeggs {
32c39f472eSBen Skeggs return mxm->mxms;
33c39f472eSBen Skeggs
34c39f472eSBen Skeggs }
35c39f472eSBen Skeggs
36c39f472eSBen Skeggs u16
mxms_version(struct nvkm_mxm * mxm)37be83cd4eSBen Skeggs mxms_version(struct nvkm_mxm *mxm)
38c39f472eSBen Skeggs {
39c39f472eSBen Skeggs u8 *mxms = mxms_data(mxm);
40c39f472eSBen Skeggs u16 version = (mxms[4] << 8) | mxms[5];
41c39f472eSBen Skeggs switch (version ) {
42c39f472eSBen Skeggs case 0x0200:
43c39f472eSBen Skeggs case 0x0201:
44c39f472eSBen Skeggs case 0x0300:
45c39f472eSBen Skeggs return version;
46c39f472eSBen Skeggs default:
47c39f472eSBen Skeggs break;
48c39f472eSBen Skeggs }
49c39f472eSBen Skeggs
5027cc60a1SBen Skeggs nvkm_debug(&mxm->subdev, "unknown version %d.%d\n", mxms[4], mxms[5]);
51c39f472eSBen Skeggs return 0x0000;
52c39f472eSBen Skeggs }
53c39f472eSBen Skeggs
54c39f472eSBen Skeggs u16
mxms_headerlen(struct nvkm_mxm * mxm)55be83cd4eSBen Skeggs mxms_headerlen(struct nvkm_mxm *mxm)
56c39f472eSBen Skeggs {
57c39f472eSBen Skeggs return 8;
58c39f472eSBen Skeggs }
59c39f472eSBen Skeggs
60c39f472eSBen Skeggs u16
mxms_structlen(struct nvkm_mxm * mxm)61be83cd4eSBen Skeggs mxms_structlen(struct nvkm_mxm *mxm)
62c39f472eSBen Skeggs {
63c39f472eSBen Skeggs return *(u16 *)&mxms_data(mxm)[6];
64c39f472eSBen Skeggs }
65c39f472eSBen Skeggs
66c39f472eSBen Skeggs bool
mxms_checksum(struct nvkm_mxm * mxm)67be83cd4eSBen Skeggs mxms_checksum(struct nvkm_mxm *mxm)
68c39f472eSBen Skeggs {
69c39f472eSBen Skeggs u16 size = mxms_headerlen(mxm) + mxms_structlen(mxm);
70c39f472eSBen Skeggs u8 *mxms = mxms_data(mxm), sum = 0;
71c39f472eSBen Skeggs while (size--)
72c39f472eSBen Skeggs sum += *mxms++;
73c39f472eSBen Skeggs if (sum) {
7427cc60a1SBen Skeggs nvkm_debug(&mxm->subdev, "checksum invalid\n");
75c39f472eSBen Skeggs return false;
76c39f472eSBen Skeggs }
77c39f472eSBen Skeggs return true;
78c39f472eSBen Skeggs }
79c39f472eSBen Skeggs
80c39f472eSBen Skeggs bool
mxms_valid(struct nvkm_mxm * mxm)81be83cd4eSBen Skeggs mxms_valid(struct nvkm_mxm *mxm)
82c39f472eSBen Skeggs {
83c39f472eSBen Skeggs u8 *mxms = mxms_data(mxm);
84c39f472eSBen Skeggs if (*(u32 *)mxms != 0x5f4d584d) {
8527cc60a1SBen Skeggs nvkm_debug(&mxm->subdev, "signature invalid\n");
86c39f472eSBen Skeggs return false;
87c39f472eSBen Skeggs }
88c39f472eSBen Skeggs
89c39f472eSBen Skeggs if (!mxms_version(mxm) || !mxms_checksum(mxm))
90c39f472eSBen Skeggs return false;
91c39f472eSBen Skeggs
92c39f472eSBen Skeggs return true;
93c39f472eSBen Skeggs }
94c39f472eSBen Skeggs
95c39f472eSBen Skeggs bool
mxms_foreach(struct nvkm_mxm * mxm,u8 types,bool (* exec)(struct nvkm_mxm *,u8 *,void *),void * info)96be83cd4eSBen Skeggs mxms_foreach(struct nvkm_mxm *mxm, u8 types,
97be83cd4eSBen Skeggs bool (*exec)(struct nvkm_mxm *, u8 *, void *), void *info)
98c39f472eSBen Skeggs {
9927cc60a1SBen Skeggs struct nvkm_subdev *subdev = &mxm->subdev;
100c39f472eSBen Skeggs u8 *mxms = mxms_data(mxm);
101c39f472eSBen Skeggs u8 *desc = mxms + mxms_headerlen(mxm);
102c39f472eSBen Skeggs u8 *fini = desc + mxms_structlen(mxm) - 1;
103c39f472eSBen Skeggs while (desc < fini) {
104c39f472eSBen Skeggs u8 type = desc[0] & 0x0f;
105c39f472eSBen Skeggs u8 headerlen = 0;
106c39f472eSBen Skeggs u8 recordlen = 0;
107c39f472eSBen Skeggs u8 entries = 0;
108c39f472eSBen Skeggs
109c39f472eSBen Skeggs switch (type) {
110c39f472eSBen Skeggs case 0: /* Output Device Structure */
111c39f472eSBen Skeggs if (mxms_version(mxm) >= 0x0300)
112c39f472eSBen Skeggs headerlen = 8;
113c39f472eSBen Skeggs else
114c39f472eSBen Skeggs headerlen = 6;
115c39f472eSBen Skeggs break;
116c39f472eSBen Skeggs case 1: /* System Cooling Capability Structure */
117c39f472eSBen Skeggs case 2: /* Thermal Structure */
118c39f472eSBen Skeggs case 3: /* Input Power Structure */
119c39f472eSBen Skeggs headerlen = 4;
120c39f472eSBen Skeggs break;
121c39f472eSBen Skeggs case 4: /* GPIO Device Structure */
122c39f472eSBen Skeggs headerlen = 4;
123c39f472eSBen Skeggs recordlen = 2;
124c39f472eSBen Skeggs entries = (ROM32(desc[0]) & 0x01f00000) >> 20;
125c39f472eSBen Skeggs break;
126c39f472eSBen Skeggs case 5: /* Vendor Specific Structure */
127c39f472eSBen Skeggs headerlen = 8;
128c39f472eSBen Skeggs break;
129c39f472eSBen Skeggs case 6: /* Backlight Control Structure */
130c39f472eSBen Skeggs if (mxms_version(mxm) >= 0x0300) {
131c39f472eSBen Skeggs headerlen = 4;
132c39f472eSBen Skeggs recordlen = 8;
133c39f472eSBen Skeggs entries = (desc[1] & 0xf0) >> 4;
134c39f472eSBen Skeggs } else {
135c39f472eSBen Skeggs headerlen = 8;
136c39f472eSBen Skeggs }
137c39f472eSBen Skeggs break;
138c39f472eSBen Skeggs case 7: /* Fan Control Structure */
139c39f472eSBen Skeggs headerlen = 8;
140c39f472eSBen Skeggs recordlen = 4;
141c39f472eSBen Skeggs entries = desc[1] & 0x07;
142c39f472eSBen Skeggs break;
143c39f472eSBen Skeggs default:
14427cc60a1SBen Skeggs nvkm_debug(subdev, "unknown descriptor type %d\n", type);
145c39f472eSBen Skeggs return false;
146c39f472eSBen Skeggs }
147c39f472eSBen Skeggs
148a4f7bd36SBen Skeggs if (mxm->subdev.debug >= NV_DBG_DEBUG && (exec == NULL)) {
14927cc60a1SBen Skeggs static const char * mxms_desc[] = {
150c39f472eSBen Skeggs "ODS", "SCCS", "TS", "IPS",
151c39f472eSBen Skeggs "GSD", "VSS", "BCS", "FCS",
152c39f472eSBen Skeggs };
153c39f472eSBen Skeggs u8 *dump = desc;
15427cc60a1SBen Skeggs char data[32], *ptr;
155c39f472eSBen Skeggs int i, j;
156c39f472eSBen Skeggs
15727cc60a1SBen Skeggs for (j = headerlen - 1, ptr = data; j >= 0; j--)
15827cc60a1SBen Skeggs ptr += sprintf(ptr, "%02x", dump[j]);
159c39f472eSBen Skeggs dump += headerlen;
160c39f472eSBen Skeggs
16127cc60a1SBen Skeggs nvkm_debug(subdev, "%4s: %s\n", mxms_desc[type], data);
162c39f472eSBen Skeggs for (i = 0; i < entries; i++, dump += recordlen) {
16327cc60a1SBen Skeggs for (j = recordlen - 1, ptr = data; j >= 0; j--)
16427cc60a1SBen Skeggs ptr += sprintf(ptr, "%02x", dump[j]);
16527cc60a1SBen Skeggs nvkm_debug(subdev, " %s\n", data);
166c39f472eSBen Skeggs }
167c39f472eSBen Skeggs }
168c39f472eSBen Skeggs
169c39f472eSBen Skeggs if (types & (1 << type)) {
170c39f472eSBen Skeggs if (!exec(mxm, desc, info))
171c39f472eSBen Skeggs return false;
172c39f472eSBen Skeggs }
173c39f472eSBen Skeggs
174c39f472eSBen Skeggs desc += headerlen + (entries * recordlen);
175c39f472eSBen Skeggs }
176c39f472eSBen Skeggs
177c39f472eSBen Skeggs return true;
178c39f472eSBen Skeggs }
179c39f472eSBen Skeggs
180c39f472eSBen Skeggs void
mxms_output_device(struct nvkm_mxm * mxm,u8 * pdata,struct mxms_odev * desc)181be83cd4eSBen Skeggs mxms_output_device(struct nvkm_mxm *mxm, u8 *pdata, struct mxms_odev *desc)
182c39f472eSBen Skeggs {
183c39f472eSBen Skeggs u64 data = ROM32(pdata[0]);
184c39f472eSBen Skeggs if (mxms_version(mxm) >= 0x0300)
185c39f472eSBen Skeggs data |= (u64)ROM16(pdata[4]) << 32;
186c39f472eSBen Skeggs
187c39f472eSBen Skeggs desc->outp_type = (data & 0x00000000000000f0ULL) >> 4;
188c39f472eSBen Skeggs desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8;
189c39f472eSBen Skeggs desc->conn_type = (data & 0x000000000001f000ULL) >> 12;
190c39f472eSBen Skeggs desc->dig_conn = (data & 0x0000000000780000ULL) >> 19;
191c39f472eSBen Skeggs }
192