1*8f6fb765Smacallan /* $NetBSD: psoc.c,v 1.8 2021/06/18 23:00:47 macallan Exp $ */
24492437aSmacallan
34492437aSmacallan /*-
439745b2aSmacallan * Copyright (c) 2019 Michael Lorenz
54492437aSmacallan * All rights reserved.
64492437aSmacallan *
74492437aSmacallan * Redistribution and use in source and binary forms, with or without
84492437aSmacallan * modification, are permitted provided that the following conditions
94492437aSmacallan * are met:
104492437aSmacallan * 1. Redistributions of source code must retain the above copyright
114492437aSmacallan * notice, this list of conditions and the following disclaimer.
124492437aSmacallan * 2. Redistributions in binary form must reproduce the above copyright
134492437aSmacallan * notice, this list of conditions and the following disclaimer in the
144492437aSmacallan * documentation and/or other materials provided with the distribution.
154492437aSmacallan *
164492437aSmacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
174492437aSmacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184492437aSmacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
194492437aSmacallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
204492437aSmacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214492437aSmacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224492437aSmacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234492437aSmacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244492437aSmacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254492437aSmacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
264492437aSmacallan * POSSIBILITY OF SUCH DAMAGE.
274492437aSmacallan */
284492437aSmacallan
294492437aSmacallan /*
304492437aSmacallan * fan controller found in 1GHz TiBook
314492437aSmacallan *
324492437aSmacallan * register values from OF:
334492437aSmacallan * fan1 - 0x20 ( status ), 0x31 ( data )
344492437aSmacallan * fan2 - 0x26 ( status ), 0x45 ( data )
3557ada224Smacallan * fan status byte 0:
3657ada224Smacallan * 0x5* - fan is running, 0x6* - fan stopped
3757ada224Smacallan * byte 1: unknown, 0x80 seems always set, lower bits seem to fluctuate
3857ada224Smacallan * byte 2: lower 6 bit seem to indicate speed
3957ada224Smacallan * fan speed may be lower 6 bit of byte 2 and lower 6 of byte 1
404492437aSmacallan * temperature sensors start at 6, two bytes each, first appears to be
414492437aSmacallan * the temperature in degrees Celsius
424492437aSmacallan */
434492437aSmacallan
444492437aSmacallan #include <sys/cdefs.h>
45*8f6fb765Smacallan __KERNEL_RCSID(0, "$NetBSD: psoc.c,v 1.8 2021/06/18 23:00:47 macallan Exp $");
464492437aSmacallan
474492437aSmacallan #include <sys/param.h>
484492437aSmacallan #include <sys/systm.h>
494492437aSmacallan #include <sys/device.h>
504492437aSmacallan #include <sys/conf.h>
514492437aSmacallan #include <sys/bus.h>
524492437aSmacallan #include <sys/time.h>
534492437aSmacallan
544492437aSmacallan #include <dev/ofw/openfirm.h>
554492437aSmacallan
564492437aSmacallan #include <dev/i2c/i2cvar.h>
574492437aSmacallan
584492437aSmacallan #include <dev/sysmon/sysmonvar.h>
594492437aSmacallan
60be8eeb4cSmacallan #include "opt_psoc.h"
61be8eeb4cSmacallan #ifdef PSOC_DEBUG
62be8eeb4cSmacallan #define DPRINTF printf
63be8eeb4cSmacallan #else
64be8eeb4cSmacallan #define DPRINTF if (0) printf
65be8eeb4cSmacallan #endif
66be8eeb4cSmacallan
674492437aSmacallan struct psoc_softc {
684492437aSmacallan device_t sc_dev;
694492437aSmacallan i2c_tag_t sc_i2c;
704492437aSmacallan i2c_addr_t sc_addr;
714492437aSmacallan int sc_node;
724492437aSmacallan
734492437aSmacallan struct sysmon_envsys *sc_sme;
744492437aSmacallan envsys_data_t sc_sensors[7];
754492437aSmacallan int sc_nsensors;
764492437aSmacallan uint8_t sc_temp[16];
774492437aSmacallan time_t sc_last;
784492437aSmacallan };
794492437aSmacallan
804492437aSmacallan static int psoc_match(device_t, cfdata_t, void *);
814492437aSmacallan static void psoc_attach(device_t, device_t, void *);
824492437aSmacallan
834492437aSmacallan static void psoc_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
844492437aSmacallan
8557ada224Smacallan static void psoc_dump(struct psoc_softc *);
8657ada224Smacallan
874492437aSmacallan CFATTACH_DECL_NEW(psoc, sizeof(struct psoc_softc),
884492437aSmacallan psoc_match, psoc_attach, NULL, NULL);
894492437aSmacallan
904492437aSmacallan static const struct device_compatible_entry compat_data[] = {
9156caee62Sthorpej { .compat = "Psoc" },
92ec189949Sthorpej DEVICE_COMPAT_EOL
934492437aSmacallan };
944492437aSmacallan
954492437aSmacallan static int
psoc_match(device_t parent,cfdata_t match,void * aux)964492437aSmacallan psoc_match(device_t parent, cfdata_t match, void *aux)
974492437aSmacallan {
984492437aSmacallan struct i2c_attach_args *ia = aux;
994492437aSmacallan int match_result;
1004492437aSmacallan
1014492437aSmacallan if (iic_use_direct_match(ia, match, compat_data, &match_result))
1024492437aSmacallan return match_result;
1034492437aSmacallan
1044492437aSmacallan return 0;
1054492437aSmacallan }
1064492437aSmacallan
1074492437aSmacallan static void
psoc_attach(device_t parent,device_t self,void * aux)1084492437aSmacallan psoc_attach(device_t parent, device_t self, void *aux)
1094492437aSmacallan {
1104492437aSmacallan struct psoc_softc *sc = device_private(self);
1114492437aSmacallan struct i2c_attach_args *ia = aux;
1124492437aSmacallan char path[256];
1134492437aSmacallan envsys_data_t *s;
11457ada224Smacallan int error, ih, r, i;
1154492437aSmacallan
1164492437aSmacallan sc->sc_dev = self;
1174492437aSmacallan sc->sc_i2c = ia->ia_tag;
1184492437aSmacallan sc->sc_addr = ia->ia_addr;
1194492437aSmacallan sc->sc_node = ia->ia_cookie;
1204492437aSmacallan sc->sc_last = 0;
1214492437aSmacallan
1224492437aSmacallan aprint_naive("\n");
1234492437aSmacallan aprint_normal(": Psoc fan controller\n");
1244492437aSmacallan
1254492437aSmacallan error = OF_package_to_path(sc->sc_node, path, 256);
1264492437aSmacallan path[error] = 0;
127be8eeb4cSmacallan DPRINTF("path [%s]\n", path);
1284492437aSmacallan ih = OF_open("fan");
1294492437aSmacallan OF_call_method_1("fan-init", ih, 0);
130be8eeb4cSmacallan DPRINTF("ih %08x\n", ih);
1314492437aSmacallan
1324492437aSmacallan sc->sc_sme = sysmon_envsys_create();
1334492437aSmacallan sc->sc_sme->sme_name = device_xname(self);
1344492437aSmacallan sc->sc_sme->sme_cookie = sc;
1354492437aSmacallan sc->sc_sme->sme_refresh = psoc_sensors_refresh;
1364492437aSmacallan sc->sc_nsensors = 0;
1374492437aSmacallan
13857ada224Smacallan psoc_dump(sc);
13957ada224Smacallan
1404492437aSmacallan for (i = 0; i < 4; i++) {
1414492437aSmacallan r = i * 2 + 6;
1424492437aSmacallan s = &sc->sc_sensors[sc->sc_nsensors];
1434492437aSmacallan s->state = ENVSYS_SINVALID;
1444492437aSmacallan s->units = ENVSYS_STEMP;
1454492437aSmacallan snprintf(s->desc, 16, "temp%d", i);
1464492437aSmacallan s->private = r;
1474492437aSmacallan sysmon_envsys_sensor_attach(sc->sc_sme, s);
1484492437aSmacallan sc->sc_nsensors++;
1494492437aSmacallan }
15057ada224Smacallan
15157ada224Smacallan for (r = 0x20; r < 0x2b; r += 0x06) {
1524492437aSmacallan s = &sc->sc_sensors[sc->sc_nsensors];
1534492437aSmacallan s->state = ENVSYS_SINVALID;
1544492437aSmacallan s->units = ENVSYS_SFANRPM;
1554492437aSmacallan snprintf(s->desc, 16, "reg %02x", r);
1564492437aSmacallan s->private = r;
1574492437aSmacallan sysmon_envsys_sensor_attach(sc->sc_sme, s);
1584492437aSmacallan sc->sc_nsensors++;
1594492437aSmacallan }
16057ada224Smacallan
1614492437aSmacallan sysmon_envsys_register(sc->sc_sme);
1624492437aSmacallan }
1634492437aSmacallan
1644492437aSmacallan static void
psoc_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)1654492437aSmacallan psoc_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
1664492437aSmacallan {
1674492437aSmacallan struct psoc_softc *sc = sme->sme_cookie;
1684492437aSmacallan uint8_t cmd = 6;
16939745b2aSmacallan uint8_t buf[0x28];
17057ada224Smacallan int error = 1, data;
1714492437aSmacallan
1724492437aSmacallan if ( edata->private < 0x20) {
1734492437aSmacallan cmd = 0;
1744492437aSmacallan if ((time_second - sc->sc_last) > 2) {
1754492437aSmacallan iic_acquire_bus(sc->sc_i2c, 0);
1764492437aSmacallan error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
1774492437aSmacallan sc->sc_addr, &cmd, 1, sc->sc_temp, 16, 0);
1784492437aSmacallan iic_release_bus(sc->sc_i2c, 0);
1794492437aSmacallan if (error) return;
1804492437aSmacallan sc->sc_last = time_second;
1814492437aSmacallan }
1824492437aSmacallan error = 0;
1834492437aSmacallan if (edata->private > 0) {
1844492437aSmacallan data = sc->sc_temp[edata->private];
1854492437aSmacallan /* Celsius -> microkelvin */
1864492437aSmacallan edata->value_cur = ((int)data * 1000000) + 273150000;
1874492437aSmacallan }
18857ada224Smacallan #ifdef PSOC_DEBUG
18957ada224Smacallan if (edata->private == 6)
19057ada224Smacallan psoc_dump(sc);
19157ada224Smacallan #endif
1924492437aSmacallan } else {
19339745b2aSmacallan cmd = edata->private;
1944492437aSmacallan iic_acquire_bus(sc->sc_i2c, 0);
1954492437aSmacallan error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
19639745b2aSmacallan sc->sc_addr, &cmd, 1, buf, 3, 0);
1974492437aSmacallan iic_release_bus(sc->sc_i2c, 0);
1984492437aSmacallan if (error) return;
19939745b2aSmacallan switch (buf[0] & 0xf0) {
20057ada224Smacallan case 0x50:
20139745b2aSmacallan data = buf[edata->private - 0x20];
20239745b2aSmacallan edata->value_cur = ((buf[2] & 0x3f) << 6) |
20339745b2aSmacallan (buf[1] & 0x3f);
20457ada224Smacallan break;
20557ada224Smacallan case 0x60:
20657ada224Smacallan edata->value_cur = 0;
20757ada224Smacallan break;
20857ada224Smacallan default:
20939745b2aSmacallan error = 0;
2104492437aSmacallan }
2114492437aSmacallan }
2124492437aSmacallan if (error) {
2134492437aSmacallan edata->state = ENVSYS_SINVALID;
2144492437aSmacallan } else {
2154492437aSmacallan edata->state = ENVSYS_SVALID;
2164492437aSmacallan }
2174492437aSmacallan }
21857ada224Smacallan
21957ada224Smacallan static void
psoc_dump(struct psoc_softc * sc)22057ada224Smacallan psoc_dump(struct psoc_softc *sc)
22157ada224Smacallan {
22257ada224Smacallan int i, j;
22357ada224Smacallan uint8_t data, cmd;
224*8f6fb765Smacallan
225*8f6fb765Smacallan iic_acquire_bus(sc->sc_i2c, 0);
22657ada224Smacallan for (i = 0x20; i < 0x5f; i+= 8) {
22757ada224Smacallan printf("%02x:", i);
22857ada224Smacallan for (j = 0; j < 8; j++) {
22957ada224Smacallan cmd = i + j;
23057ada224Smacallan data = 0;
23157ada224Smacallan iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
23257ada224Smacallan sc->sc_addr, &cmd, 1, &data, 1, 0);
23357ada224Smacallan printf(" %02x", data);
23457ada224Smacallan }
23557ada224Smacallan printf("\n");
23657ada224Smacallan }
237*8f6fb765Smacallan iic_release_bus(sc->sc_i2c, 0);
23857ada224Smacallan }
239