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

lm63.c (8149B)


      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 Texas Instruments LM63
      8  * The LM63 is a Remote Diode Digital Temperature Sensor with Integrated Fan
      9  * Control temperature sensor with an I2C bus interface. The host can query
     10  * the LM63 at any time to read the temperature. For more information,
     11  * see the Texas Instruments LM63 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 
     19 
     20 #define LM63_REG_TEMP        0x00
     21 
     22 /*
     23  * Convert from hardware values to degree C.
     24  *
     25  * Note that you should be a bit careful with which arguments
     26  * these macros are called: arguments may be evaluated more than once.
     27  *
     28  * TEMP_FROM_REG converts hardware register value to software temperature
     29  * value. Bits[7:0]. This 8-bit value is in 2's complement format, with an
     30  * LSB equal to 1 degree C, the range can be from -128 to +127 degree C.
     31  * So, to ease the conversion we typecast the value to signed char.
     32  * Software value is also in fixed value format, each unit represents 1
     33  * C, the range of the converted value can be from -128 to +127.
     34  */
     35 #define TEMP_FROM_REG(val) ((signed char)val)
     36 
     37 /* Initial values */
     38 #define LM63_OP_READ         0x1
     39 #define LM63_OP_WRITE        0x2
     40 
     41 /*
     42  * LM63 Hardware information : registers and their human-readable
     43  * counterparts.
     44  */
     45 typedef struct lm63_s {
     46     uint8    temp;        /* Hardware info */
     47     int temp_val;
     48 } lm63_t;
     49 
     50 /*
     51  * Function: lm63_ioctl
     52  *
     53  * Purpose: read local temperature data from the LM63 temperature chip.
     54  *
     55  * Parameters:
     56  *    unit - StrataSwitch device number or I2C bus number
     57  *    devno - chip device id
     58  *    opcode - CPU request (LM63_OP_READ)
     59  *    data - address of lm63_t data
     60  *    len - size of test data (not used)
     61  *
     62  * Returns:
     63  *     SOC_E_NONE - no error
     64  *     SOC_E_TIMEOUT - chip temperature reading unavailable or IO error.
     65  */
     66 STATIC int
     67 lm63_ioctl(int unit, int devno, int opcode, void* data, int len)
     68 {
     69     int r = SOC_E_NONE;
     70     lm63_t* l = (lm63_t*)data;
     71     uint8 saddr = soc_i2c_addr(unit, devno);
     72 
     73     if (opcode == LM63_OP_READ) {
     74 
     75         r = soc_i2c_read_byte_data(unit, saddr, LM63_REG_TEMP, &l->temp);
     76         if (r < 0) {
     77             return r;
     78         }
     79 
     80         soc_i2c_device(unit, devno)->rbyte += 1;
     81 
     82         /* Convert the register values to temperature values */
     83         l->temp_val = TEMP_FROM_REG(l->temp);
     84     }
     85     return r;
     86 }
     87 
     88 /*
     89  * LM63 control structure.
     90  */
     91 typedef struct {
     92     int    sleep;                /* Thread sleep time (0 to exit) */
     93     int    temp[MAX_I2C_DEVICES];        /* last temp values */
     94 } lm63_dev_info_t;
     95 
     96 static lm63_dev_info_t *lm63_info[SOC_MAX_NUM_DEVICES];
     97 
     98 #define C2F(_d) ((9*(_d)/5) + 32)   /* celsius to fahrenheit */
     99 #define MINDIFF 1                   /* at least 1 degree change */
    100 
    101 /*
    102  * Function: lm63_temp_show
    103  *
    104  * Purpose:  Query LM63 chip for temperature and report status in
    105  *           degrees Celsius and Degrees Fahrenheit.
    106  *
    107  * Parameters:
    108  *    unit    - switch unit
    109  *    dev    - i2c device
    110  *    force    - if true, show temp even if it hasn't changed
    111  */
    112 STATIC void
    113 lm63_temp_show(int unit, int dev, int force)
    114 {
    115     lm63_t        lm63;
    116     lm63_dev_info_t    *lmi;
    117     int            ctemp, ftemp, lasttemp;
    118     int            difftemp;
    119 
    120     lmi = lm63_info[unit];
    121     if (lmi == NULL) {
    122         return;
    123     }
    124 
    125     /* Read back stats */
    126     if (lm63_ioctl(unit, dev, LM63_OP_READ, &lm63, sizeof(lm63_t)) < 0) {
    127         LOG_CLI((BSL_META_U(unit, "%s: ERROR: device not responding\n"),
    128                  soc_i2c_devname(unit, dev)));
    129         lmi->sleep = 0;        /* exit thread */
    130         return;
    131     }
    132 
    133     ctemp = lm63.temp_val;
    134     lasttemp = lmi->temp[dev];
    135 
    136     if (lasttemp == 0) {    /* first time through */
    137         difftemp = MINDIFF;
    138     } else {
    139         difftemp = lasttemp - ctemp;
    140         if (difftemp < 0) {
    141             difftemp = -difftemp;
    142         }
    143     }
    144 
    145     if (force || difftemp >= MINDIFF) {
    146     lmi->temp[dev] = ctemp;
    147 
    148     /* 20 degree C change: something is wrong */
    149     if (difftemp > 20) {
    150         if (force) {
    151             LOG_CLI((BSL_META_U(unit, "%s: NOTICE: Temperature Unavailable\n"),
    152                      soc_i2c_devname(unit, dev)));
    153             if (lasttemp > 0) {
    154                 ftemp = C2F(lasttemp);
    155                 LOG_CLI((BSL_META_U(unit, "%s: Last Temperature %dC, %dF\n"),
    156                          soc_i2c_devname(unit, dev),
    157                          lasttemp, ftemp));
    158             }
    159         }
    160         return;
    161     }
    162 
    163     ftemp = C2F(ctemp);
    164 
    165     LOG_CLI((BSL_META_U(unit, "%s: Temperature %dC, %dF\n"),
    166                  soc_i2c_devname(unit, dev),
    167                  ctemp, ftemp));
    168     }
    169 }
    170 
    171 /*
    172  * Function: lm63_thread
    173  *
    174  * Purpose: Poll LM63 devices and report stats every poll_delay seconds
    175  *
    176  * Parameters:
    177  *    unitp    - void * version of unit
    178  */
    179 STATIC void
    180 lm63_thread(void *unitp)
    181 {
    182     int    unit, dev, ndev;
    183     lm63_dev_info_t    *lmi;
    184 
    185     unit = PTR_TO_INT(unitp);
    186     lmi = lm63_info[unit];
    187 
    188     while (lmi && lmi->sleep) {
    189         ndev = soc_i2c_device_count(unit);
    190         for (dev = 0; dev < ndev; dev++) {
    191             if (soc_i2c_devtype(unit, dev) == LM63_DEVICE_TYPE) {
    192                 lm63_temp_show(unit, dev, FALSE);
    193             }
    194         }
    195         sal_usleep(lmi->sleep * SECOND_USEC);
    196     }
    197     LOG_CLI((BSL_META_U(unit, "unit %d: Thermal monitoring completed\n"), unit));
    198     sal_thread_exit(0);
    199 }
    200 
    201 
    202 STATIC int
    203 lm63_init(int unit, int devno, void* data, int len)
    204 {
    205     soc_i2c_devdesc_set(unit, devno, "LM63 Temperature Sensor");
    206 
    207     /* Init devinfo */
    208     if (lm63_info[unit] == NULL) {
    209         lm63_info[unit] = sal_alloc(sizeof(lm63_dev_info_t), "lm63_info");
    210         if (lm63_info[unit] == NULL) {
    211             return SOC_E_MEMORY;
    212         }
    213         sal_memset(lm63_info[unit], 0, sizeof(lm63_dev_info_t));
    214     }
    215     return SOC_E_NONE;
    216 }
    217 
    218 /*
    219  * Function: soc_i2c_lm63_monitor
    220  *
    221  * Purpose:
    222  *     Monitor configuration: start or stop a temperature probe
    223  *     task to monitor temperatures every nsecs.
    224  * Parameters:
    225  *    unit - StrataSwitch device number or I2C bus number
    226  *    enable - start or stop a temperature probe task
    227  *    nsecs - number of seconds delay, between succesive queries.
    228  *  Returns:
    229  *    None
    230  *  Notes:
    231  *    None
    232  */
    233 void
    234 soc_i2c_lm63_monitor(int unit, int enable, int nsecs)
    235 {
    236     if (!soc_i2c_is_attached(unit)) {
    237         soc_i2c_attach(unit, 0, 0);
    238     }
    239     if (lm63_info[unit] == NULL) {
    240         return;
    241     }
    242     if (!enable) {
    243         lm63_info[unit]->sleep = 0;
    244         return;
    245     }
    246     if (lm63_info[unit]->sleep) {
    247         return;
    248     }
    249     lm63_info[unit]->sleep = nsecs;
    250 
    251     sal_thread_create("bcmTEMP",
    252               SAL_THREAD_STKSZ,
    253               50,
    254               (void (*)(void*))lm63_thread,
    255               INT_TO_PTR(unit));
    256     LOG_CLI((BSL_META_U(unit, "unit %d: Thermal monitoring enabled\n"), unit));
    257 }
    258 
    259 /*
    260  * Function: soc_i2c_lm63_temperature_show
    261  *
    262  * Purpose: Show all temperature stats on all chips.
    263  *
    264  * Parameters:
    265  *   unit - StrataSwitch device number or I2C bus number
    266  *
    267  * Returns:
    268  *   None
    269  */
    270 void
    271 soc_i2c_lm63_temperature_show(int unit)
    272 {
    273     int dev, ndev;
    274 
    275     if (!soc_i2c_is_attached(unit)) {
    276         soc_i2c_attach(unit, 0, 0);
    277     }
    278     if (lm63_info[unit] == NULL) {
    279         return;
    280     }
    281     ndev = soc_i2c_device_count(unit);
    282     for (dev = 0; dev < ndev; dev++) {
    283         if (soc_i2c_devtype(unit, dev) == LM63_DEVICE_TYPE) {
    284             lm63_temp_show(unit, dev, TRUE);
    285         }
    286     }
    287 }
    288 
    289 /* NOT USED */
    290 STATIC int
    291 lm63_read(int unit, int devno, uint16 addr, uint8* data, uint32* len)
    292 {
    293     return SOC_E_NONE;
    294 }
    295 
    296 /* NOT USED */
    297 STATIC int
    298 lm63_write(int unit, int devno, uint16 addr, uint8* data, uint32 len)
    299 {
    300     return SOC_E_NONE;
    301 }
    302 
    303 /* LM63 Temperature Sensor Driver callout */
    304 i2c_driver_t _soc_i2c_lm63_driver = {
    305     0x0, 0x0, /* System assigned bytes */
    306     LM63_DEVICE_TYPE,
    307     lm63_read,
    308     lm63_write,
    309     lm63_ioctl,
    310     lm63_init,
    311     NULL,
    312 };