uboot/drivers/misc/pca9551_led.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2015 Stefan Roese <sr@denx.de>
   4 */
   5
   6#include <common.h>
   7#include <errno.h>
   8#include <i2c.h>
   9#include <status_led.h>
  10
  11#ifndef CONFIG_PCA9551_I2C_ADDR
  12#error "CONFIG_PCA9551_I2C_ADDR not defined!"
  13#endif
  14
  15#define PCA9551_REG_INPUT       0x00    /* Input register (read only) */
  16#define PCA9551_REG_PSC0        0x01    /* Frequency prescaler 0 */
  17#define PCA9551_REG_PWM0        0x02    /* PWM0 */
  18#define PCA9551_REG_PSC1        0x03    /* Frequency prescaler 1 */
  19#define PCA9551_REG_PWM1        0x04    /* PWM1 */
  20#define PCA9551_REG_LS0         0x05    /* LED0 to LED3 selector */
  21#define PCA9551_REG_LS1         0x06    /* LED4 to LED7 selector */
  22
  23#define PCA9551_CTRL_AI         (1 << 4)        /* Auto-increment flag */
  24
  25#define PCA9551_LED_STATE_ON            0x00
  26#define PCA9551_LED_STATE_OFF           0x01
  27#define PCA9551_LED_STATE_BLINK0        0x02
  28#define PCA9551_LED_STATE_BLINK1        0x03
  29
  30struct pca9551_blink_rate {
  31        u8 psc; /* Frequency preescaler, see PCA9551_7.pdf p. 6 */
  32        u8 pwm; /* Pulse width modulation, see PCA9551_7.pdf p. 6 */
  33};
  34
  35static int freq_last = -1;
  36static int mask_last = -1;
  37static int idx_last = -1;
  38static int mode_last;
  39
  40static int pca9551_led_get_state(int led, int *state)
  41{
  42        unsigned int reg;
  43        u8 shift, buf;
  44        int ret;
  45
  46        if (led < 0 || led > 7) {
  47                return -EINVAL;
  48        } else if (led < 4) {
  49                reg = PCA9551_REG_LS0;
  50                shift = led << 1;
  51        } else {
  52                reg = PCA9551_REG_LS1;
  53                shift = (led - 4) << 1;
  54        }
  55
  56        ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
  57        if (ret)
  58                return ret;
  59
  60        *state = (buf >> shift) & 0x03;
  61        return 0;
  62}
  63
  64static int pca9551_led_set_state(int led, int state)
  65{
  66        unsigned int reg;
  67        u8 shift, buf, mask;
  68        int ret;
  69
  70        if (led < 0 || led > 7) {
  71                return -EINVAL;
  72        } else if (led < 4) {
  73                reg = PCA9551_REG_LS0;
  74                shift = led << 1;
  75        } else {
  76                reg = PCA9551_REG_LS1;
  77                shift = (led - 4) << 1;
  78        }
  79        mask = 0x03 << shift;
  80
  81        ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
  82        if (ret)
  83                return ret;
  84
  85        buf = (buf & ~mask) | ((state & 0x03) << shift);
  86
  87        ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
  88        if (ret)
  89                return ret;
  90
  91        return 0;
  92}
  93
  94static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
  95{
  96        unsigned int reg;
  97        int ret;
  98
  99        switch (idx) {
 100        case 0:
 101                reg = PCA9551_REG_PSC0;
 102                break;
 103        case 1:
 104                reg = PCA9551_REG_PSC1;
 105                break;
 106        default:
 107                return -EINVAL;
 108        }
 109        reg |= PCA9551_CTRL_AI;
 110
 111        ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
 112        if (ret)
 113                return ret;
 114
 115        return 0;
 116}
 117
 118/*
 119 * Functions referenced by cmd_led.c or status_led.c
 120 */
 121void __led_init(led_id_t id, int state)
 122{
 123}
 124
 125void __led_set(led_id_t mask, int state)
 126{
 127        if (state == CONFIG_LED_STATUS_OFF)
 128                pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
 129        else
 130                pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
 131}
 132
 133void __led_toggle(led_id_t mask)
 134{
 135        int state = 0;
 136
 137        pca9551_led_get_state(mask, &state);
 138        pca9551_led_set_state(mask, !state);
 139}
 140
 141void __led_blink(led_id_t mask, int freq)
 142{
 143        struct pca9551_blink_rate rate;
 144        int mode;
 145        int idx;
 146
 147        if ((freq == freq_last) || (mask == mask_last)) {
 148                idx = idx_last;
 149                mode = mode_last;
 150        } else {
 151                /* Toggle blink index */
 152                if (idx_last == 0) {
 153                        idx = 1;
 154                        mode = PCA9551_LED_STATE_BLINK1;
 155                } else {
 156                        idx = 0;
 157                        mode = PCA9551_LED_STATE_BLINK0;
 158                }
 159
 160                idx_last = idx;
 161                mode_last = mode;
 162        }
 163        freq_last = freq;
 164        mask_last = mask;
 165
 166        rate.psc = ((freq * 38) / 1000) - 1;
 167        rate.pwm = 128;         /* 50% duty cycle */
 168
 169        pca9551_led_set_blink_rate(idx, rate);
 170        pca9551_led_set_state(mask, mode);
 171}
 172