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 
269936aeeaSIlia Mirkin #define ROM16(x) get_unaligned_le16(&(x))
279936aeeaSIlia 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