openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

lm75.c (12430B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * BCM56xx I2C Device Driver for National Semiconductor LM75
      8  * The LM75 is a temperature sensor, Delta-Sigma ADC, and digital
      9  * over-temp detector with an I2C bus interface. The host can query
     10  * the LM75 at any time to read the temperature. For more information,
     11  * see the National Semiconductor LM75 Datasheet.
     12  */
     13 #include <sal/types.h>
     14 #include <soc/drv.h>
     15 #include <soc/error.h>
     16 #include <soc/i2c.h>
     17 #include <shared/bsl.h>
     18 /* The LM75 registers */
     19 #define LM75_REG_TEMP		0x00
     20 #define LM75_REG_CONF		0x01
     21 #define LM75_REG_THYST		0x02
     22 #define LM75_REG_TOS		0x03
     23 
     24 /*
     25  * Function: lm75_limit
     26  *
     27  * Purpose:  Range check the input value.
     28  *
     29  * Parameters:
     30  *   value - input comparator value
     31  *   low - low value
     32  *   high - high value
     33  *
     34  * Returns:
     35  *    value or high or low value if out of range.
     36  */
     37 STATIC INLINE int
     38 lm75_limit(long value, long low, long high)
     39 {
     40     if (value < low)
     41 	return low;
     42     else if (value > high)
     43 	return high;
     44     else
     45 	return value;
     46 }
     47 
     48 /*
     49  * Function: lm75_swap
     50  *
     51  * Purpose: byte swap a 16 bit value returned from LM75
     52  *
     53  * Parameters:
     54  *    value - input 16 bit value.
     55  *
     56  * Returns:
     57  *    byte swapped value.
     58  */
     59 STATIC uint16
     60 lm75_swap(uint16 val)
     61 {
     62     return (val >> 8) | (val << 8);
     63 }
     64 
     65 /*
     66  * Convert from hardware values to tenths of a degree C.
     67  *
     68  * Rounding and limit checking is only done on the TO_REG variants.
     69  * Note that you should be a bit careful with which arguments
     70  * these macros are called: arguments may be evaluated more than once.
     71  *
     72  * TEMP_FROM_REG converts hardware register value to software temperature
     73  * value. Bit[15:7] (i.e. the most significant 9 bit) of hardware register is
     74  * the temperature. These 9-bit value is in 2's complement format, each unit
     75  * represents 0.5 degree C, the range can be from -128.0 to +127.5 degree C.
     76  * Softwrae value is also in fixed value format, each unit represents 0.1
     77  * C, the range of the converted value can be from -1280 to +1275. The
     78  * conversion first signed extended the 16 bit signed value to 32 bit signed
     79  * value to avoid overflow. Using arithmatic division by 128 instead of right
     80  * shift 7 bit to avoid the complier implementation-dependent behavior on
     81  * most significant filled bits on right shift operation.
     82  */
     83 #define TEMP_FROM_REG(val) ((int)(int16)val * 5 / 128)
     84 #define TEMP_TO_REG(val)   (lm75_limit((val<0?(0x200+\
     85 			     ((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff))
     86 
     87 /* Initial values */
     88 #define LM75_INIT_TEMP_OS    600	/* overtemp shutdown: 60.0 */
     89 #define LM75_INIT_TEMP_HYST  500	/* clear alarm temp: 50.0 */
     90 #define LM75_OP_READ         0x1
     91 #define LM75_OP_WRITE        0x2
     92 
     93 /*
     94  * LM75 Hardware information : registers and their human-readable
     95  * counterparts.
     96  */
     97 typedef struct lm75_s {
     98     uint16	temp;		/* Hardware info */
     99     uint8	conf;
    100     uint16	t_hist;
    101     uint16	t_os;
    102     int		temp_val;	/* Human readable values in fixed point */
    103     int		hist_val;
    104     int		os_val;
    105 } lm75_t;
    106 
    107 /*
    108  * Function: lm75_ioctl
    109  *
    110  * Purpose: read temperature data from the LM75 temperature probe
    111  *          chip.
    112  *
    113  * Parameters:
    114  *    unit - StrataSwitch device number or I2C bus number
    115  *    devno - chip device id
    116  *    opcode - CPU request (LM75_OP_READ)
    117  *    data - address of lm75_t data
    118  *    len - size of test data (not used)
    119  *
    120  * Returns:
    121  *     SOC_E_NONE - no error
    122  *     SOC_E_TIMEOUT - chip temperature reading unavailable or IO error.
    123  */
    124 STATIC int
    125 lm75_ioctl(int unit, int devno,
    126 	   int opcode, void* data, int len)
    127 {
    128     int r = SOC_E_NONE;
    129     lm75_t* l = (lm75_t*)data;
    130     uint8 saddr = soc_i2c_addr(unit, devno);
    131     int t_os, t_hist;
    132 
    133     if (opcode == LM75_OP_READ) {
    134 
    135 	r = soc_i2c_read_word_data(unit, saddr, LM75_REG_TEMP, &l->temp);
    136 	if (r < 0) {
    137 	    return r;
    138 	}
    139 
    140 	soc_i2c_device(unit, devno)->rbyte += 2;
    141 
    142 	r = soc_i2c_read_word_data(unit, saddr, LM75_REG_TOS, &l->t_os);
    143 	if (r < 0) {
    144 	    return r;
    145 	}
    146 
    147 	soc_i2c_device(unit, devno)->rbyte += 2;
    148 
    149 	r = soc_i2c_read_word_data(unit, saddr, LM75_REG_THYST, &l->t_hist);
    150 	if (r < 0) {
    151 	    return r;
    152 	}
    153 
    154 	soc_i2c_device(unit, devno)->rbyte += 2;
    155 
    156 	/* Byte swap the 16 bit registers */
    157 	l->temp = lm75_swap(l->temp);
    158 	l->t_hist = lm75_swap(l->t_hist);
    159 	l->t_os = lm75_swap(l->t_os);
    160 
    161 	/* Convert the register values to temperature values */
    162 	l->temp_val = TEMP_FROM_REG(l->temp);
    163 	l->hist_val = TEMP_FROM_REG(l->t_hist);
    164 	l->os_val = TEMP_FROM_REG(l->t_os);
    165     } else {
    166 	/* Write regvalues to HW */
    167 	t_os = l->t_os;
    168 	t_hist = l->t_hist;
    169 	/* Convert to register format */
    170 	t_hist = TEMP_TO_REG(t_hist);
    171 	t_os = TEMP_TO_REG(t_os);
    172 	/* Byte swap */
    173 	t_hist = lm75_swap(t_hist);
    174 	t_os = lm75_swap(t_os);
    175 
    176 	/* Feed back to chip */
    177 	r = soc_i2c_write_word_data(unit, saddr, LM75_REG_TOS, t_os);
    178 	if (r < 0) {
    179 	    return r;
    180 	}
    181 
    182 	soc_i2c_device(unit, devno)->tbyte += 2;
    183 
    184 	r = soc_i2c_write_word_data(unit, saddr, LM75_REG_THYST, t_hist);
    185 	if (r < 0) {
    186 	    return r;
    187 	}
    188 
    189 	soc_i2c_device(unit, devno)->tbyte += 2;
    190 
    191 	r = soc_i2c_write_byte_data(unit, saddr, LM75_REG_TEMP, l->conf);
    192 	if (r < 0) {
    193 	    return r;
    194 	}
    195 
    196 	soc_i2c_device(unit, devno)->tbyte++;
    197     }
    198     return r;
    199 }
    200 
    201 /*
    202  * LM75 control structure.
    203  */
    204 typedef struct {
    205     int	sleep;				/* Thread sleep time (0 to exit) */
    206     int	temp[MAX_I2C_DEVICES];		/* last temp values */
    207 } lm75_dev_info_t;
    208 
    209 static lm75_dev_info_t *lm75_info[SOC_MAX_NUM_DEVICES];
    210 
    211 #define	C2F(_d)		((9*(_d)/5) + 320)	/* celsius*10 to farenheit*10 */
    212 
    213 #define	MINDIFF		10			/* at least 1 degree change */
    214 
    215 /*
    216  * Function: lm75_temp_show
    217  *
    218  * Purpose:  Query LM75 chip for temperature and report status in
    219  *           degrees Celcius and Degrees Fahrenheit.
    220  *
    221  * Parameters:
    222  *	unit	- switch unit
    223  *	dev	- i2c device
    224  *	force	- if true, show temp even if it hasn't changed
    225  */
    226 STATIC void
    227 lm75_temp_show(int unit, int dev, int force)
    228 {
    229     lm75_t		lm75;
    230     lm75_dev_info_t	*lmi;
    231     int			ctemp, ftemp, lasttemp;
    232     int			difftemp, alarm_temp, shut_temp;
    233 
    234     lmi = lm75_info[unit];
    235     if (lmi == NULL) {
    236 	return;
    237     }
    238 
    239     /* Read back stats */
    240     if (lm75_ioctl(unit, dev, LM75_OP_READ, &lm75, sizeof(lm75_t)) < 0) {
    241         LOG_CLI((BSL_META_U(unit,
    242                             "%s: ERROR: device not responding\n"),
    243                  soc_i2c_devname(unit, dev)));
    244         lmi->sleep = 0;		/* exit thread */
    245         return;
    246     }
    247 
    248     ctemp = lm75.temp_val;
    249     lasttemp = lmi->temp[dev];
    250 
    251     if (lasttemp == 0) {	/* first time through */
    252 	difftemp = MINDIFF;
    253     } else {
    254 	difftemp = lasttemp - ctemp;
    255 	if (difftemp < 0) {
    256 	    difftemp = -difftemp;
    257 	}
    258     }
    259 
    260     if (force || difftemp >= MINDIFF) {
    261 	lmi->temp[dev] = ctemp;
    262 	alarm_temp = lm75.hist_val;
    263 	shut_temp = lm75.os_val;
    264 
    265 	/* 20 degree C change: something is wrong */
    266 	if (difftemp > 200) {
    267 	    if (force) {
    268             LOG_CLI((BSL_META_U(unit,
    269                                 "%s: NOTICE: temperature unavailable\n"),
    270                      soc_i2c_devname(unit, dev)));
    271             if (lasttemp > 0) {
    272                 ftemp = C2F(lasttemp);
    273                 LOG_CLI((BSL_META_U(unit,
    274                                     "%s: last temperature %d.%dC, %d.%dF\n"),
    275                          soc_i2c_devname(unit, dev),
    276                          lasttemp/10, lasttemp%10,
    277                          ftemp/10, ftemp%10));
    278             }
    279 	    }
    280 	    return;
    281 	}
    282 
    283 	ftemp = C2F(ctemp);
    284 
    285 	if (ctemp > shut_temp) {
    286 	    LOG_CLI((BSL_META_U(unit,
    287                                 "%s: temperature %d.%dC, %d.%dF "
    288                                 "WARNING: over shutdown %d.%dC\n"),
    289                      soc_i2c_devname(unit, dev),
    290                      ctemp/10, ctemp%10,
    291                      ftemp/10, ftemp%10,
    292                      shut_temp/10, shut_temp%10));
    293 	} else if (ctemp > alarm_temp) {
    294 	    LOG_CLI((BSL_META_U(unit,
    295                                 "%s: temperature %d.%dC, %d.%dF "
    296                                 "WARNING: over alarm %d.%dC\n"),
    297                      soc_i2c_devname(unit, dev),
    298                      ctemp/10, ctemp%10,
    299                      ftemp/10, ftemp%10,
    300                      alarm_temp/10, alarm_temp%10));
    301 	} else {
    302 	    LOG_CLI((BSL_META_U(unit,
    303                                 "%s: temperature %d.%dC, %d.%dF\n"),
    304                      soc_i2c_devname(unit, dev),
    305                      ctemp/10, ctemp%10,
    306                      ftemp/10, ftemp%10));
    307 	}
    308 
    309     }
    310 }
    311 
    312 /*
    313  * Function: lm75_thread
    314  *
    315  * Purpose: Poll LM75 devices and report stats every poll_delay seconds
    316  *
    317  * Parameters:
    318  *    unitp	- void * version of unit
    319  */
    320 STATIC void
    321 lm75_thread(void *unitp)
    322 {
    323     int			unit, dev, ndev;
    324     lm75_dev_info_t	*lmi;
    325 
    326     unit = PTR_TO_INT(unitp);
    327     lmi = lm75_info[unit];
    328 
    329     while (lmi && lmi->sleep) {
    330 	ndev = soc_i2c_device_count(unit);
    331 	for (dev = 0; dev < ndev; dev++) {
    332 	    if (soc_i2c_devtype(unit, dev) == LM75_DEVICE_TYPE) {
    333 		lm75_temp_show(unit, dev, FALSE);
    334 	    }
    335 	}
    336 	sal_usleep(lmi->sleep * SECOND_USEC);
    337     }
    338     LOG_CLI((BSL_META_U(unit,
    339                         "unit %d: thermal monitoring completed\n"), unit));
    340     sal_thread_exit(0);
    341 }
    342 
    343 /*
    344  * Function: lm75_init
    345  *
    346  * Purpose: Initialize the LM75 temperature sensor chip.
    347  *
    348  * Parameters:
    349  *    unit - StrataSwitch device number or I2C bus number
    350  *    devno - chip device id
    351  *    data - not used
    352  *    len - not used
    353  *
    354  * Returns:
    355  *     SOC_E_NONE - no failure
    356  *     SOC_E_TIMEOUT - chip or device access failure.
    357  */
    358 STATIC int
    359 lm75_init(int unit, int devno,
    360 	  void* data, int len)
    361 {
    362     int		rv;
    363     lm75_t	lm75;
    364 
    365     soc_i2c_devdesc_set(unit, devno, "LM75 Temperature Sensor");
    366 
    367     /* Init chip */
    368     lm75.t_hist = LM75_INIT_TEMP_HYST;
    369     lm75.t_os = LM75_INIT_TEMP_OS;
    370     lm75.conf = 0;
    371     rv = lm75_ioctl(unit, devno, LM75_OP_WRITE, &lm75, sizeof(lm75_t));
    372     if (rv < 0) {
    373         LOG_CLI((BSL_META_U(unit,
    374                             "%s: ERROR: device did not initialize: %s\n"),
    375                  soc_i2c_devname(unit, devno),
    376                  soc_errmsg(rv)));
    377     }
    378 
    379     /* Init devinfo */
    380     if (lm75_info[unit] == NULL) {
    381 	lm75_info[unit] = sal_alloc(sizeof(lm75_dev_info_t), "lm75_info");
    382 	if (lm75_info[unit] == NULL) {
    383 	    return SOC_E_MEMORY;
    384 	}
    385 	sal_memset(lm75_info[unit], 0, sizeof(lm75_dev_info_t));
    386     }
    387 
    388     return SOC_E_NONE;
    389 }
    390 
    391 /*
    392  * Function: soc_i2c_lm75_monitor
    393  *
    394  * Purpose:
    395  *     Monitor configuration: start or stop a temperature probe
    396  *     task to monitor temperatures every nsecs.
    397  * Parameters:
    398  *    unit - StrataSwitch device number or I2C bus number
    399  *    enable - start or stop a temperature probe task
    400  *    nsecs - number of seconds delay, between succesive queries.
    401  *  Returns:
    402  *    None
    403  *  Notes:
    404  *    None
    405  */
    406 void
    407 soc_i2c_lm75_monitor(int unit, int enable, int nsecs)
    408 {
    409     if (!soc_i2c_is_attached(unit)) {
    410 	soc_i2c_attach(unit, 0, 0);
    411     }
    412     if (lm75_info[unit] == NULL) {
    413 	return;
    414     }
    415     if (!enable) {
    416 	lm75_info[unit]->sleep = 0;
    417 	return;
    418     }
    419     if (lm75_info[unit]->sleep) {
    420 	return;
    421     }
    422     lm75_info[unit]->sleep = nsecs;
    423 
    424     sal_thread_create("bcmTEMP",
    425 		      SAL_THREAD_STKSZ,
    426 		      50,
    427 		      (void (*)(void*))lm75_thread,
    428 		      INT_TO_PTR(unit));
    429     LOG_CLI((BSL_META_U(unit,
    430                         "unit %d: thermal monitoring enabled\n"), unit));
    431 }
    432 
    433 
    434 /*
    435  * Function: soc_i2c_lm75_temperature_show
    436  *
    437  * Purpose: Show all temperature stats on all chips.
    438  *
    439  * Parameters:
    440  *   unit - StrataSwitch device number or I2C bus number
    441  *
    442  * Returns:
    443  *   None
    444  */
    445 void
    446 soc_i2c_lm75_temperature_show(int unit)
    447 {
    448     int		dev, ndev;
    449 
    450     if (!soc_i2c_is_attached(unit)) {
    451 	soc_i2c_attach(unit, 0, 0);
    452     }
    453     if (lm75_info[unit] == NULL) {
    454 	return;
    455     }
    456     ndev = soc_i2c_device_count(unit);
    457     for (dev = 0; dev < ndev; dev++) {
    458 	if (soc_i2c_devtype(unit, dev) == LM75_DEVICE_TYPE) {
    459 	    lm75_temp_show(unit, dev, TRUE);
    460 	}
    461     }
    462 }
    463 
    464 /* NOT USED */
    465 STATIC int
    466 lm75_read(int unit, int devno,
    467 	  uint16 addr, uint8* data, uint32* len)
    468 {
    469     return SOC_E_NONE;
    470 }
    471 
    472 /* NOT USED */
    473 STATIC int
    474 lm75_write(int unit, int devno,
    475 	   uint16 addr, uint8* data, uint32 len)
    476 {
    477     return SOC_E_NONE;
    478 }
    479 
    480 
    481 /* LM75 Temperature Sensor Driver callout */
    482 i2c_driver_t _soc_i2c_lm75_driver = {
    483     0x0, 0x0, /* System assigned bytes */
    484     LM75_DEVICE_TYPE,
    485     lm75_read,
    486     lm75_write,
    487     lm75_ioctl,
    488     lm75_init,
    489     NULL,
    490 };