linux/drivers/leds/leds-pca955x.c
<<
>>
Prefs
   1/*
   2 * Copyright 2007-2008 Extreme Engineering Solutions, Inc.
   3 *
   4 * Author: Nate Case <ncase@xes-inc.com>
   5 *
   6 * This file is subject to the terms and conditions of version 2 of
   7 * the GNU General Public License.  See the file COPYING in the main
   8 * directory of this archive for more details.
   9 *
  10 * LED driver for various PCA955x I2C LED drivers
  11 *
  12 * Supported devices:
  13 *
  14 *      Device          Description             7-bit slave address
  15 *      ------          -----------             -------------------
  16 *      PCA9550         2-bit driver            0x60 .. 0x61
  17 *      PCA9551         8-bit driver            0x60 .. 0x67
  18 *      PCA9552         16-bit driver           0x60 .. 0x67
  19 *      PCA9553/01      4-bit driver            0x62
  20 *      PCA9553/02      4-bit driver            0x63
  21 *
  22 * Philips PCA955x LED driver chips follow a register map as shown below:
  23 *
  24 *      Control Register                Description
  25 *      ----------------                -----------
  26 *      0x0                             Input register 0
  27 *                                      ..
  28 *      NUM_INPUT_REGS - 1              Last Input register X
  29 *
  30 *      NUM_INPUT_REGS                  Frequency prescaler 0
  31 *      NUM_INPUT_REGS + 1              PWM register 0
  32 *      NUM_INPUT_REGS + 2              Frequency prescaler 1
  33 *      NUM_INPUT_REGS + 3              PWM register 1
  34 *
  35 *      NUM_INPUT_REGS + 4              LED selector 0
  36 *      NUM_INPUT_REGS + 4
  37 *          + NUM_LED_REGS - 1          Last LED selector
  38 *
  39 *  where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many
  40 *  bits the chip supports.
  41 */
  42
  43#include <linux/module.h>
  44#include <linux/delay.h>
  45#include <linux/string.h>
  46#include <linux/ctype.h>
  47#include <linux/leds.h>
  48#include <linux/err.h>
  49#include <linux/i2c.h>
  50#include <linux/workqueue.h>
  51#include <linux/slab.h>
  52
  53/* LED select registers determine the source that drives LED outputs */
  54#define PCA955X_LS_LED_ON       0x0     /* Output LOW */
  55#define PCA955X_LS_LED_OFF      0x1     /* Output HI-Z */
  56#define PCA955X_LS_BLINK0       0x2     /* Blink at PWM0 rate */
  57#define PCA955X_LS_BLINK1       0x3     /* Blink at PWM1 rate */
  58
  59enum pca955x_type {
  60        pca9550,
  61        pca9551,
  62        pca9552,
  63        pca9553,
  64};
  65
  66struct pca955x_chipdef {
  67        int                     bits;
  68        u8                      slv_addr;       /* 7-bit slave address mask */
  69        int                     slv_addr_shift; /* Number of bits to ignore */
  70};
  71
  72static struct pca955x_chipdef pca955x_chipdefs[] = {
  73        [pca9550] = {
  74                .bits           = 2,
  75                .slv_addr       = /* 110000x */ 0x60,
  76                .slv_addr_shift = 1,
  77        },
  78        [pca9551] = {
  79                .bits           = 8,
  80                .slv_addr       = /* 1100xxx */ 0x60,
  81                .slv_addr_shift = 3,
  82        },
  83        [pca9552] = {
  84                .bits           = 16,
  85                .slv_addr       = /* 1100xxx */ 0x60,
  86                .slv_addr_shift = 3,
  87        },
  88        [pca9553] = {
  89                .bits           = 4,
  90                .slv_addr       = /* 110001x */ 0x62,
  91                .slv_addr_shift = 1,
  92        },
  93};
  94
  95static const struct i2c_device_id pca955x_id[] = {
  96        { "pca9550", pca9550 },
  97        { "pca9551", pca9551 },
  98        { "pca9552", pca9552 },
  99        { "pca9553", pca9553 },
 100        { }
 101};
 102MODULE_DEVICE_TABLE(i2c, pca955x_id);
 103
 104struct pca955x {
 105        struct mutex lock;
 106        struct pca955x_led *leds;
 107        struct pca955x_chipdef  *chipdef;
 108        struct i2c_client       *client;
 109};
 110
 111struct pca955x_led {
 112        struct pca955x  *pca955x;
 113        struct work_struct      work;
 114        enum led_brightness     brightness;
 115        struct led_classdev     led_cdev;
 116        int                     led_num;        /* 0 .. 15 potentially */
 117        char                    name[32];
 118};
 119
 120/* 8 bits per input register */
 121static inline int pca95xx_num_input_regs(int bits)
 122{
 123        return (bits + 7) / 8;
 124}
 125
 126/* 4 bits per LED selector register */
 127static inline int pca95xx_num_led_regs(int bits)
 128{
 129        return (bits + 3)  / 4;
 130}
 131
 132/*
 133 * Return an LED selector register value based on an existing one, with
 134 * the appropriate 2-bit state value set for the given LED number (0-3).
 135 */
 136static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
 137{
 138        return (oldval & (~(0x3 << (led_num << 1)))) |
 139                ((state & 0x3) << (led_num << 1));
 140}
 141
 142/*
 143 * Write to frequency prescaler register, used to program the
 144 * period of the PWM output.  period = (PSCx + 1) / 38
 145 */
 146static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
 147{
 148        struct pca955x *pca955x = i2c_get_clientdata(client);
 149
 150        i2c_smbus_write_by_bydatapan>
/*
>
inline int pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdefpca95xx_num_led_reg>  3uct apan>
/* 141<>>
  512

 142 142val
 144 142 147
  518
   9   9  41pca95511,
pca955x_write_psc(struct i2c_client *client, int n, u8   51,
pca95531,
 148        struct pca955x *pca955x = i2c_get_clientdata(};
  615
 150        i2c_smbus_write_by_bydatapan>
/* {
inline int pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdefpca95xx_num_led_reg>  1>  3uct apan>
/*bits1;
  68        
  70<1/a>};
   1  711
 133   1  51] = {
/* Le" nED select  2-bit state value set for the given LED number a95501] = {
  41pca955x_chipdef pca955x_write_psc(struct i2c_client *client, int n, u8   610x60,
 148        struct pca955x *pca955x = i2c_get_clientdata(1] = {
 150        i2c_smbus_write_by_bydatapan>
/*inline int pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdefpca95xx_num_led_reg>  4>  ct apan>
/* 10x60,
1 = 3,
   1 133   1/* Le" nED select  2-bit state value set for the given LED number shift1 = 3,
  41inline pca955x_chipdef pca955x_write_psc(struct i2c_client *u8  148        struct pca955x *pca955x = i2c_get_clientdata( 150         15ite_by_bydata" class="sref">i2c_smbus_write_by_bydatapan>
/*     1   },
inline int pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdefpca95xx_num_led_reg>  4>  ct  href="+code=client" class="sref"sa(  914
[1] = {
woline" na 0x3ct" ode=pca955x_write_psc" clase" name="L113"> 113        struct work_strucsref">u8 woline" na 0x  struct woline" na 0x  sta( 148        struct pca955x(inline pca955x_chipdl    b2a>       2{ }
pca955x_chipdx_ch1lf   led_num)lass="line" name="L41">  41 101};
wolsa 0x   slv_addED sin LSxe" nusepriat)lass="line" name="L41">  41 1032/a>
woline" na 0x  stra> *pca955xonta"drr_osx_numct work_struc,8" id="L148" class="line" namea 0xclass="sref">woline" na 0x  st>client, int ct" class="sref">work_strucsa href="+code=brightness" class="sref">b2a53"> 1032/
pca955x *woline" na 0x  st-="compca955x_chipdede=pca955x" class="sref">pca955x((pca955x_chipdx_ch1lf    *woline" na 0x  st-="compca955x_chipdelsref">state & 0x3) <&lrn (bits<2>chipdef<2a>;
wolsa 0x    *woline" na 0x  st-="compca955x_chipdelsref">state & 0x3) <&l%n (bits<2>ster X;
 109};
  _="+code=mutex" clas/a>  _="+cx_num     ct pca955x<-="compca955x_chipde="+code=mutex" class="sref"sa href="+code=brightness" class="sref">b2110"> 1102/a>
pca955x_chipdl     *pca955x_chipdef pca955x<-="compca955x_chipdefclient" class="sref">i2c_client<*pca955x_chipdx_ch1lf   b21below:;
work<2a>;
woline" na 0x  st-="compca955x_chipde" class="sref">led_brightness     <)href="+code=pca955x_led" class="sref">pca2ightness<2a>;
led_brightf="+FULLa955:ef="+code=pca955x_led" class="sref">pca2i------;
pca955x_chipdl     *u8 pca955x_chipdl    wolsa 0x     54#define b21ster 0
b21chipdef<22];
  55#def="+code=PC:ef="+code=pca955x_led" class="sref">pca2ister X};
pca955x_chipdl     *u8 pca955x_chipdl    wolsa 0x     55#define b2119"> 1192/a>
b2ter */
  55#def="+HALLe=PC:ef="+code=pca955x_led" class="sref">pca2ef">bits<2a>)
pca955x_chipdl     *u8 pca955x_chipdl    wolsa 0x     56#define b21below:{
b2tf">work<2 8;
pca2eghtness<2a>}
   1 1252/a>
   1 142bits<2a>)
 142 142 142  41 1312/a>
 state &line" name="L14w>ode=pct pca955x<-="compca955x_chipdefclient" class="sref">i2c_clientslv_addr_2nt">/*
 *woline" na 0x  st-="compca955x_chipde" class="sref">led_brightness     <)a href="+code=brightness" class="sref">b2, with
pca955x_chipdl     *u8 pca955x_chipdl    wolsa 0x     57#define b2,ghtness<2an>
b2t"> */
state<2a>)
bits<2a>{
pca955x_chipdef pca955x<-="compca955x_chipdefclient" class="sref">i2c_client<*pca955x_chipdx_ch1lf   pca955x_chipdl    <)a href="+code=brightness" class="sref">b2,ster X  _un="+code=mutex" clas/a>  _un="+cx_num     ct pca955x<-="compca955x_chipde="+code=mutex" class="sref"sa href="+code=brightness" class="sref">b240"> 140<2a>}
 1412/a>
/*
woline" na 0x3claode=pca955x_write_psc" clasl" name="L115"> 115        struct led_classdev  ,4" id="L114" class="line" name="L114"> 114        enum  114    n clatrucsref">u8 pca2) / 38
woline" na 0x  struct pca955x( */
val<2a>)
pca955x *pca955xonta"drr_osx_numct led_classdev  ,4" id="L148" class="line" namea 0xclass="sref">woline" na 0x  st>client, int lv" class="sref">led_classdev  sa href="+code=brightness" class="sref">b24f">bits<2a>{
client);
pca955x<-="compca955x_chipde" class="sref">led_brightness      * 114    n clatruca href="+code=brightness" class="sref">b2149"> 1492/a>
   1 1412>>
 142  522
 142  41woschedule3ct" x_num     ct pca955x<-="compca955x_chipdect" class="sref">work_strucsa href="+code=brightness" class="sref">b2HI-Z */
val<2span>
 147<2span>
wo__trucniaef="+"L114" class="lline" naproba4"> 114    line" naprobaode=pca955x_write_psc" class="sref">pca955x_write_psc(struct i2c_client.c#L124" id="L124" class="line" name="L2Hclient
  95static const struct static consdtrucsref">u8 pca25an>
,
 148        struct pca955x(pca95512,
woline" na 0x  struct woline" na 0x  sta(  52,
 107        struct pca955x_chipdx_ch  sta(,
  90  st sadapte"="+co href="+code=i2cadapte"L90">  90  adapte"="+ca(};
 150         15  struct 
  90  s  st>client, int er"L90">  90  er"  sta(val<2/a> {
bits2;
pca955x_chipdx_ch  st/a>     ct pca955x_chipdef <static consdtruc-="compca955x_chipded="L12"> 150         15truc        
  90  adapte"="+c/a> *  90  to_st sadapte"x_numct i2c_clien-="compca955x_chipdedass="sref">led_clv  L91" class="lineparient" class="sref"parientrucsa href="+code=brightness" class="sref">b2nore */
 150         15  st/a> *i2c_clien-="compca955x_chipdedass="sref">led_clv  L91" class="lineplats="m"> 150         15  sta href="+code=brightness" class="sref">b2nan>
};
  721
   /name="typU comboent"> *is possible *ss="line" name="L41">  41  52] = {
i2c_clien-="compca955x_chipdeaddrL90">  90  add"="+c/      ~((1lt; (pca955x_chipdx_ch  st-="compca955x_chipdeslv_add"_shifnt" class="sref"slv_add"_shifntrucs -ref= !=" name="L41">  41pca955x_chipdx_ch  st-="compca955x_chipdeslv_add"t" class="sref"slv_add"trucs ref="+code=pca955x_led" class="sref">pca2         2 = 2,
  90  das_er"x_num     ct i2c_clien-="compca955x_chipdedass="sref">led_clv  ,9" id="L99" class="line" naminvalid1slavmeaddr>   %02x\n>  99     ef="+code=pca955x_led" class="sref">pca2 I-Z */i2c_clien-="compca955x_chipdeaddrL90">  90  add"="+csa href="+code=brightness" class="sref">b2nef">val<2ppan>
  90  ENODEV  sta href="+code=brightness" class="sref">b2nef">bits2   },
wols="t x_numct woKERN_INFOef="+" id="L99" class="line" namss="line" na: Usturn%  %d4"> 1ED sid="L1 at >  99    x.c#L149" id="L149" class="line" name="2 an>
   0x%02x\n>  99     ef="+code=pca955x_led" class="sref">pca2shift2 = 3,
static consdtruc-="compca955x_chipdeiver5static coniverlient<*pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_ret<*i2c_clien-="compca955x_chipdeaddrL90">  90  add"="+csa href="+code=brightness" class="sref">b2     2   },
  90  adapte"="+ct<*  90  I2C_FUNC_I2C="+cssref">u8 woEIO  sta href="+code=brightness" class="sref">b2/ 20x60,
 150         15  sts ref="+code=pca955x_led" class="sref">pca2     2   },
  68         150         15  st-="compca955x_chipdeif="+cosref">pca95xx_nuif="+cos8">  !a> *pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_res ref="+code=pca955x_led" class="sref">pca2<95512] = {
  90  das_er"x_num     ct i2c_clien-="compca955x_chipdedass="sref">led_clv  ,9" id="L99" class="line" namboard info"L99im  %d f="s>  99    x.c#L149" id="L149" class="line" name="2         2 = 4,
 1x_ch\n>  99     ef="+code=pca955x_led" class="sref">pca2  20x62,
pca95xx_nuif="+cos8"> t<*pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_resa href="+code=brightness" class="sref">b2shift2 = 1,
  90  ENODEV  sta href="+code=brightness" class="sref">b2     2   },
[2] = {
pca955x * 146staticvi2c_clien-="compca955x_chipdedass="sref">led_clv  ,9sizeof(uct pca955x<)t<*led_brightGFP_KERNELd_resa href="+code=brightness" class="sref">b2shift2a> },
pca955x<) href="+code=brightness" class="sref">b2s/a>     2a> },
  68        <" id="L-mpca955x_chipdeENOMEML90">  90  ENOMEM  sta href="+code=brightness" class="sref">b2<95512ppan>
pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8">  a> * 146staticvi2c_clien-="compca955x_chipdedass="sref">led_clv  ,x.c#L149" id="L149" class="line" name="3c19553woline" na 0x  st) *<*pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_ret<*led_brightGFP_KERNELd_resa href="+code=brightness" class="sref">b31"> 101};
pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> ) href="+code=brightness" class="sref">b31below:);
  90  ENOMEM  sta href="+code=brightness" class="sref">b3103"> 1033/a>
 1033/
pca955x =si2c_get_clientdat<*pca955x<)a href="+code=brightness" class="sref">b3163"> 1033/ = {
wo/a>  _cniax_num     ct pca955x<-="compca955x_chipde="+code=mutex" class="sref"sa href="+code=brightness" class="sref">b3>chipdef<3a>;
pca955x<-="compca955x_chipdefclient" class="sref">i2c_clien/a> *i2c_cliena href="+code=brightness" class="sref">b3>9hipdef<3apan>
pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdef *pca955x_chipdx_ch  sta( 109};
 1103/a>
  90  s  st/a>0hr*  90  s  st/pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_rehr*  90  s  st++s ref="+code=pca955x_led" class="sref">pca355x_led {
 < 0xclass="sref">woline" na 0x  stra>     ct pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st        ;
woline" na 0x  st-="compca955x_chipdelsref">state & 0x3) <&la> *  90  s  st        1033a>;
woline" na 0x  st-="compca955x_chipdede=pca955x" class="sref">pca955x *pca955x(;
  41 150         15  sts ref="+code=pca955x_led" class="sref">pca31chipdef<32];
  68        <91" id="if13" id="L150" clad> 150         15  st-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="lineiver5static coniverlien) href="+code=brightness" class="sref">b3ister X};
pca955snls="tse=i2c_get_clientdatae=inlmea 0xclass="sref">woline" na 0x  st-="compca955x_chipdeiver5static coniverlient href="+code=brightness" class="sref">b3119"> 1193/a>
woline" na 0x  st-="compca955x_chipdeiver5static coniverlien),9" id="L99" class="line" namine" na:% >  99     ef="+code=pca955x_led" class="sref">pca3ter */
pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="lineiver5static coniverlien)a(bits<3a>)
 150         15  st-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linedefault_trigg="s="sref">led_clvb31below:{
woline" na 0x  st-="compca955x_chipdelsreclass="sref">led_classdev  L91" class="linedefault_trigg="s="sref">led_clv  41work<3 8;
pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linedefault_trigg="s="sref">led_clv(pca3125"> 1253/a>
pca955snls="tse=i2c_get_clientdatae=inlmea 0xclass="sref">woline" na 0x  st-="compca955x_chipdeiver5static coniverlient"sizeof(_get_clientdatae=inlmea 0xclass="sref">woline" na 0x  st-="compca955x_chipdeiver5static coniverlien),ef="+code=pca955x_led" class="sref">pca31ster 0
  99     > *  90  s  st)a(  68        woline" na 0x  st-="compca955x_chipdelsreclass="sref">led_classdev  L91" class="lineiver5static coniverlien/a> *woline" na 0x  st-="compca955x_chipdeiver5static coniverliena(woline" na 0x  st-="compca955x_chipdelsreclass="sref">led_classdev  L91" class="lineness  3claclass="sref">woness  3clalien/a> *woline" na 0x3claode=a(bits<3/a>
/*
woINIT_WORKx_num     ct woline" na 0x  st-="compca955x_chipdect" class="sref">work_struc,8 *woline" na 0x3ct" ode=)a(
  90  er"  st/a> *led_classdetruct <_3i2c_clien-="compca955x_chipdedass="sref">led_clv  ,x.c#L149" id="L149" class="line" name="3t"> */
woline" na 0x  st-="compca955x_chipdelsreclass="sref">led_classdev  )a(  90  er"  st/b3ff">bits<3a>{
  68        <91" id="goto" *woexiaode=a( 140<3a>}
  41 1413/a>
  90  s  st/a>0hr*  90  s  st/inline int pca955x_chipdx_ch  st-="compca955x_chipdes="sref">pca95xx_num_led_resar*  90  s  st++s" name="L41">  41/*
pca955x_chipdef i2c_get_clientdat<*  90  s  st>c0x55)a(
  41 */
state &line" name="L14w>ode=pct i2c_get_clientdat<0,l25c-mpca955x_chipdef="+HALL55">  55#def="+HALLe=PC)a(bits<3a>{
  41state &line" name="L14w>ode=pct i2c_get_clientdat<1t<0)a( 1493/a>
  41 1413>>
 146staticline" name="L14s"ode=pct i2c_get_clientdat<0,l0)a(/*
 146staticline" name="L14s"ode=pct i2c_get_clientdat<1t<0)a(
 */
val<3span>
woexiaode=:ef="+code=pca955x_led" class="sref">pca3H7"> 147<3span>
  90  s  st--s ref="+code=pca955x_led" class="sref">pca3Hclient
led_classdetruct <_un3pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linelsreclass="sref">led_classdev  )a( 146staticcancel3ct" _syn"x_num     ct pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linect" class="sref">work_strucsa href="+code=brightness" class="sref">b35an>
,
pca95513,
  53,
  90  er"  sta href="+code=brightness" class="sref">b35m the,
};

wo__truexiaa955x62" id="L62" clline" naremovr5static conline" naremovrx_numca955x_write_psc" class="sref">pca955x_write_psc(struct i2c_cliens" name="L41">  41val<3/a> {
pca3ref">bits3;
 148        struct pca955x *pca955x =gi2c_get_clientdasa href="+code=brightness" class="sref">b35client
  90  s  sta href="+code=brightness" class="sref">b3nore */
  90  s  st/a>0hr*  90  s  st/pca955x<-="compca955x_chipdef" class="sref">pca955x_chipdefpca95xx_num_led_rehr*  90  s  st++s ref="+code=pca955x_led" class="sref">pca3"L71">  731
led_classdetruct <_un3pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linelsreclass="sref">led_classdev  )apca3"L52">  53] = {
 146staticcancel3ct" _syn"x_num     ct pca955x<-="compca955x_chipde=cosref">pca95xx_nu+cos8"> 88" id="L88" cla L90">  90  s  st L91" class="linect" class="sref">work_strucsa href="+code=brightnessclass="sref">pca3"m theval<3ppan>
bits3   },
3] = {
pca955x =" id="a955x62" id="L62" clline" na" id="sref">pca955xpca3         3 = 8,
pca955xref="+code=pca955x_led" class="sref">pca3 an>
  99     ef="+code=pca955x_led" class="sref">pca3shift3 = 3,
pca955x *pca955x  ,x.c#L149" id="L149" class="line" name="3     3   },
 114    lrobaode=95a> * 114    line" naprobaode=,x.c#L149" id="L149" class="line" name="3<        3= 16,
pca955x_chipd__truexia_he=i2c_get_clientdataline" naremovr5static conline" naremovrx_nu),ef="+code=pca955x_led" class="sref">pca3/ 30x60,
pca955x *static conline" na 9ode=,x.c#L149" id="L149" class="line" name="3val<3 = 3,
3] = {
pca955xpca955xpca3         3 = 4,
pca955xpca3 hift3 = 1,
pca955xpca3 /a>     3   },
pca955x  99    sa href="+code=brightnessclass="sref">pca3 m the};
LXR "L14unityx_nu,me="L"experi44" al brigion byahref="+comailto:cxp@ef=ux.nonu+xp@ef=ux.nox_nu.
+xp.ef=ux.no kindly ho>