1 /* $NetBSD: g760a.c,v 1.6 2020/11/26 12:53:03 skrll Exp $ */
2
3 /*-
4 * Copyright (C) 2008 A.Leo.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * The driver for the G760A FAN Speed PWM controller.
31 */
32
33 #include <sys/cdefs.h>
34
35 __KERNEL_RCSID(0, "$NetBSD: g760a.c,v 1.6 2020/11/26 12:53:03 skrll Exp $");
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/kernel.h>
41 #include <sys/conf.h>
42 #include <sys/sysctl.h>
43
44 #include <dev/sysmon/sysmonvar.h>
45
46 #include <dev/i2c/i2cvar.h>
47 #include <dev/i2c/g760areg.h>
48
49
50 struct g760a_softc {
51 device_t sc_dev;
52 struct sysmon_envsys *sc_sme;
53
54 envsys_data_t sc_sensor;
55 i2c_tag_t sc_tag;
56 int sc_addr;
57 };
58
59 static int g760a_match(device_t, struct cfdata*, void*);
60 static void g760a_attach(device_t, device_t, void*);
61 static void g760a_setup(struct g760a_softc*);
62 static uint8_t g760a_readreg(struct g760a_softc*, uint8_t);
63 static void g760a_writereg(struct g760a_softc*, uint8_t, uint8_t);
64
65 static int g760a_reg2rpm(int);
66
67 static void g760a_refresh(struct sysmon_envsys*, envsys_data_t*);
68 static int sysctl_g760a_rpm(SYSCTLFN_PROTO);
69
70 CFATTACH_DECL_NEW(g760a, sizeof(struct g760a_softc),
71 g760a_match, g760a_attach, NULL, NULL);
72
73 static int
g760a_match(device_t parent,struct cfdata * cf,void * arg)74 g760a_match(device_t parent, struct cfdata* cf, void* arg)
75 {
76 struct i2c_attach_args* ia = arg;
77
78 if (ia->ia_addr == G760A_ADDR) {
79 /*
80 * TODO: set up minimal speed?
81 */
82 return I2C_MATCH_ADDRESS_ONLY;
83 }
84
85 return 0;
86 }
87
88
89 static void
g760a_attach(device_t parent,device_t self,void * arg)90 g760a_attach(device_t parent, device_t self, void* arg)
91 {
92 struct i2c_attach_args* ia = arg;
93 struct g760a_softc* sc = device_private(self);
94
95 aprint_normal(": G760A Fan Controller\n");
96
97 sc->sc_dev = self;
98 sc->sc_tag = ia->ia_tag;
99 sc->sc_addr = ia->ia_addr;
100
101 g760a_setup(sc);
102 }
103
104
105 static int
g760a_reg2rpm(int n)106 g760a_reg2rpm(int n)
107 {
108 if(n == 255)
109 return 0;
110
111 if(n == 0)
112 return 255;
113
114 return G760A_N2RPM(n);
115 }
116
117
118 static uint8_t
g760a_readreg(struct g760a_softc * sc,uint8_t reg)119 g760a_readreg(struct g760a_softc* sc, uint8_t reg)
120 {
121 uint8_t data;
122
123 if (iic_acquire_bus(sc->sc_tag, 0)) {
124 aprint_error_dev(sc->sc_dev, "unable to acquire the iic bus\n");
125 return 0;
126 }
127
128 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, ®, 1,
129 &data, 1, 0);
130 iic_release_bus(sc->sc_tag, 0);
131
132 return data;
133 }
134
135
136 static void
g760a_writereg(struct g760a_softc * sc,uint8_t reg,uint8_t data)137 g760a_writereg(struct g760a_softc* sc, uint8_t reg, uint8_t data)
138 {
139
140 if (iic_acquire_bus(sc->sc_tag, 0)) {
141 aprint_error_dev(sc->sc_dev, "unable to acquire the iic bus\n");
142 return;
143 }
144
145 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, ®, 1,
146 &data, 1, 0);
147 iic_release_bus(sc->sc_tag, 0);
148 }
149
150
151 SYSCTL_SETUP(sysctl_g760a_setup, "sysctl g760a subtree setup")
152 {
153
154 sysctl_createv(NULL, 0, NULL, NULL,
155 CTLFLAG_PERMANENT,
156 CTLTYPE_NODE, "machdep", NULL,
157 NULL, 0, NULL, 0,
158 CTL_MACHDEP, CTL_EOL);
159 }
160
161
162 /*ARGUSED*/
163 static int
sysctl_g760a_rpm(SYSCTLFN_ARGS)164 sysctl_g760a_rpm(SYSCTLFN_ARGS)
165 {
166 int error, t;
167 struct sysctlnode node;
168 struct g760a_softc* sc;
169
170 node = *rnode;
171 sc = node.sysctl_data;
172
173 t = g760a_readreg(sc, G760A_REG_SET_CNT);
174 t = g760a_reg2rpm(t);
175
176 node.sysctl_data = &t;
177
178 error = sysctl_lookup(SYSCTLFN_CALL(&node));
179
180 if (error || newp == NULL)
181 return error;
182
183 if (t > 20000 || t < G760A_N2RPM(254))
184 return EINVAL;
185
186 t = g760a_reg2rpm(t);
187
188 g760a_writereg(sc, G760A_REG_SET_CNT, t);
189
190 return 0;
191 }
192
193
194 static void
g760a_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)195 g760a_refresh(struct sysmon_envsys* sme, envsys_data_t* edata)
196 {
197 struct g760a_softc* sc = sme->sme_cookie;
198
199 switch (edata->units) {
200 case ENVSYS_SFANRPM:
201 {
202 uint8_t n;
203
204 n = g760a_readreg(sc, G760A_REG_ACT_CNT);
205 edata->value_cur = g760a_reg2rpm(n);
206 }
207 break;
208 default:
209 aprint_error_dev(sc->sc_dev, "oops\n");
210 }
211
212 edata->state = ENVSYS_SVALID;
213 }
214
215
216 static void
g760a_setup(struct g760a_softc * sc)217 g760a_setup(struct g760a_softc* sc)
218 {
219 int error;
220 int ret;
221 const struct sysctlnode *me, *node;
222
223 sc->sc_sme = sysmon_envsys_create();
224
225 ret = sysctl_createv(NULL, 0, NULL, &me,
226 CTLFLAG_READWRITE,
227 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
228 NULL, 0, NULL, 0,
229 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
230 if (ret)
231 goto sysctl_failed;
232
233 (void)strlcpy(sc->sc_sensor.desc, "sysfan rpm",
234 sizeof(sc->sc_sensor.desc));
235 sc->sc_sensor.units = ENVSYS_SFANRPM;
236 sc->sc_sensor.state = ENVSYS_SINVALID;
237
238 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor))
239 goto out;
240
241 ret = sysctl_createv(NULL, 0, NULL, &node,
242 CTLFLAG_READWRITE,
243 CTLTYPE_INT, "rpm", sc->sc_sensor.desc,
244 sysctl_g760a_rpm, 0x42, (void*)sc, 0,
245 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
246
247 if (ret)
248 goto sysctl_failed;
249
250 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
251 sc->sc_sme->sme_cookie = sc;
252 sc->sc_sme->sme_refresh = g760a_refresh;
253
254 error = sysmon_envsys_register(sc->sc_sme);
255
256 if (error) {
257 aprint_error_dev(sc->sc_dev,
258 "unable to register with sysmon. errorcode %i\n", error);
259 goto out;
260 }
261
262 return;
263
264 sysctl_failed:
265 aprint_error_dev(sc->sc_dev,
266 "couldn't create sysctl nodes (%d)\n", ret);
267
268 out:
269 sysmon_envsys_destroy(sc->sc_sme);
270 }
271