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