linux/drivers/leds/leds-lp5562.c
<<
>>
Prefs
   1/*
   2 * LP5562 LED driver
   3 *
   4 * Copyright (C) 2013 Texas Instruments
   5 *
   6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/firmware.h>
  15#include <linux/i2c.h>
  16#include <linux/init.h>
  17#include <linux/leds.h>
  18#include <linux/module.h>
  19#include <linux/mutex.h>
  20#include <linux/platform_data/leds-lp55xx.h>
  21#include <linux/slab.h>
  22
  23#include "leds-lp55xx-common.h"
  24
  25#define LP5562_PROGRAM_LENGTH           32
  26#define LP5562_MAX_LEDS                 4
  27
  28/* ENABLE Register 00h */
  29#define LP5562_REG_ENABLE               0x00
  30#define LP5562_EXEC_ENG1_M              0x30
  31#define LP5562_EXEC_ENG2_M              0x0C
  32#define LP5562_EXEC_ENG3_M              0x03
  33#define LP5562_EXEC_M                   0x3F
  34#define LP5562_MASTER_ENABLE            0x40    /* Chip master enable */
  35#define LP5562_LOGARITHMIC_PWM          0x80    /* Logarithmic PWM adjustment */
  36#define LP5562_EXEC_RUN                 0x2A
  37#define LP5562_ENABLE_DEFAULT   \
  38        (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
  39#define LP5562_ENABLE_RUN_PROGRAM       \
  40        (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
  41
  42/* OPMODE Register 01h */
  43#define LP5562_REG_OP_MODE              0x01
  44#define LP5562_MODE_ENG1_M              0x30
  45#define LP5562_MODE_ENG2_M              0x0C
  46#define LP5562_MODE_ENG3_M              0x03
  47#define LP5562_LOAD_ENG1                0x10
  48#define LP5562_LOAD_ENG2                0x04
  49#define LP5562_LOAD_ENG3                0x01
  50#define LP5562_RUN_ENG1                 0x20
  51#define LP5562_RUN_ENG2                 0x08
  52#define LP5562_RUN_ENG3                 0x02
  53#define LP5562_ENG1_IS_LOADING(mode)    \
  54        ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
  55#define LP5562_ENG2_IS_LOADING(mode)    \
  56        ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
  57#define LP5562_ENG3_IS_LOADING(mode)    \
  58        ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
  59
  60/* BRIGHTNESS Registers */
  61#define LP5562_REG_R_PWM                0x04
  62#define LP5562_REG_G_PWM                0x03
  63#define LP5562_REG_B_PWM                0x02
  64#define LP5562_REG_W_PWM                0x0E
  65
  66/* CURRENT Registers */
  67#define LP5562_REG_R_CURRENT            0x07
  68#define LP5562_REG_G_CURRENT            0x06
  69#define LP5562_REG_B_CURRENT            0x05
  70#define LP5562_REG_W_CURRENT            0x0F
  71
  72/* CONFIG Register 08h */
  73#define LP5562_REG_CONFIG               0x08
  74#define LP5562_PWM_HF                   0x40
  75#define LP5562_PWRSAVE_EN               0x20
  76#define LP5562_CLK_INT                  0x01    /* Internal clock */
  77#define LP5562_DEFAULT_CFG              (LP5562_PWM_HF | LP5562_PWRSAVE_EN)
  78
  79/* RESET Register 0Dh */
  80#define LP5562_REG_RESET                0x0D
  81#define LP5562_RESET                    0xFF
  82
  83/* PROGRAM ENGINE Registers */
  84#define LP5562_REG_PROG_MEM_ENG1        0x10
  85#define LP5562_REG_PROG_MEM_ENG2        0x30
  86#define LP5562_REG_PROG_MEM_ENG3        0x50
  87
  88/* LEDMAP Register 70h */
  89#define LP5562_REG_ENG_SEL              0x70
  90#define LP5562_ENG_SEL_PWM              0
  91#define LP5562_ENG_FOR_RGB_M            0x3F
  92#define LP5562_ENG_SEL_RGB              0x1B    /* R:ENG1, G:ENG2, B:ENG3 */
  93#define LP5562_ENG_FOR_W_M              0xC0
  94#define LP5562_ENG1_FOR_W               0x40    /* W:ENG1 */
  95#define LP5562_ENG2_FOR_W               0x80    /* W:ENG2 */
  96#define LP5562_ENG3_FOR_W               0xC0    /* W:ENG3 */
  97
  98/* Program Commands */
  99#define LP5562_CMD_DISABLE              0x00
 100#define LP5562_CMD_LOAD                 0x15
 101#define LP5562_CMD_RUN                  0x2A
 102#define LP5562_CMD_DIRECT               0x3F
 103#define LP5562_PATTERN_OFF              0
 104
 105static inline void lp5562_wait_opmode_done(void)
 106{
 107        /* operation mode change needs to be longer than 153 us */
 108        usleep_range(200, 300);
 109}
 110
 111static inline void lp5562_wait_enable_done(void)
 112{
 113        /* it takes more 488 us to update ENABLE register */
 114        usleep_range(500, 600);
 115}
 116
 117static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
 118{
 119        u8 addr[] = {
 120                LP5562_REG_R_CURRENT,
 121                LP5562_REG_G_CURRENT,
 122                LP5562_REG_B_CURRENT,
 123                LP5562_REG_W_CURRENT,
 124        };
 125
 126        led->led_current = led_current;
 127        lp55xx_write(led->chip, addr[led->chan_nr], led_current);
 128}
 129
 130static void lp5562_load_engine(struct lp55xx_chip *chip)
 131{
 132        enum lp55xx_engine_index idx = chip->engine_idx;
 133        u8 mask[] = {
 134                [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
 135                [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
 136                [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
 137        };
 138
 139        u8 val[] = {
 140                [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
 141                [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
 142                [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
 143        };
 144
 145        lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
 146
 147        lp5562_wait_opmode_done();
 148}
 149
 150static void lp5562_stop_engine(struct lp55xx_chip *chip)
 151{
 152        lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
 153        lp5562_wait_opmode_done();
 154}
 155
 156static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
 157{
 158        int ret;
 159        u8 mode;
 160        u8 exec;
 161
 162        /* stop engine */
 163        if (!start) {
 164                lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
 165                lp5562_wait_enable_done();
 166                lp5562_stop_engine(chip);
 167                lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
 168                lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
 169                lp5562_wait_opmode_done();
 170                return;
 171        }
 172
 173        /*
 174         * To run the engine,
 175         * operation mode and enable register should updated at the same time
 176         */
 177
 178        ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
 179        if (ret)
 180                return;
 181
 182        ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
 183        if (ret)
 184                return;
 185
 186        /* change operation mode to RUN only when each engine is loading */
 187        if (LP5562_ENG1_IS_LOADING(mode)) {
 188                mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
 189                exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
 190        }
 191
 192        if (LP5562_ENG2_IS_LOADING(mode)) {
 193                mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
 194                exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
 195        }
 196
 197        if (LP5562_ENG3_IS_LOADING(mode)) {
 198                mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
 199                exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
 200        }
 201
 202        lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
 203        lp5562_wait_opmode_done();
 204
 205        lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
 206        lp5562_wait_enable_done();
 207}
 208
 209static int lp5562_update_firmware(struct lp55xx_chip *chip,
 210                                        const u8 *data, size_t size)
 211{
 212        enum lp55xx_engine_index idx = chip->engine_idx;
 213        u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
 214        u8 addr[] = {
 215                [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
 216                [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
 217                [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
 218        };
 219        unsigned cmd;
 220        char c[3];
 221        int program_size;
 222        int nrchars;
 223        int offset = 0;
 224        int ret;
 225        int i;
 226
 227        /* clear program memory before updating */
 228        for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
 229                lp55xx_write(chip, addr[idx] + i, 0);
 230
 231        i = 0;
 232        while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
 233                /* separate sscanfs because length is working only for %s */
 234                ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
 235                if (ret != 1)
 236                        goto err;
 237
 238                ret = sscanf(c, "%2x", &cmd);
 239                if (ret != 1)
 240                        goto err;
 241
 242                pattern[i] = (u8)cmd;
 243                offset += nrchars;
 244                i++;
 245        }
 246
 247        /* Each instruction is 16bit long. Check that length is even */
 248        if (i % 2)
 249                goto err;
 250
 251        program_size = i;
 252        for (i = 0; i < program_size; i++)
 253                lp55xx_write(chip, addr[idx] + i, pattern[i]);
 254
 255        return 0;
 256
 257err:
 258        dev_err(&chip->cl->dev, "wrong pattern format\n");
 259        return -EINVAL;
 260}
 261
 262static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
 263{
 264        const struct firmware *fw = chip->fw;
 265
 266        if (fw->size > LP5562_PROGRAM_LENGTH) {
 267                dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
 268                        fw->size);
 269                return;
 270        }
 271
 272        /*
 273         * Program momery sequence
 274         *  1) set engine mode to "LOAD"
 275         *  2) write firmware data into program memory
 276         */
 277
 278        lp5562_load_engine(chip);
 279        lp5562_update_firmware(chip, fw->data, fw->size);
 280}
 281
 282static int lp5562_post_init_device(struct lp55xx_chip *chip)
 283{
 284        int ret;
 285        u8 cfg = LP5562_DEFAULT_CFG;
 286
 287        /* Set all PWMs to direct control mode */
 288        ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
 289        if (ret)
 290                return ret;
 291
 292        lp5562_wait_opmode_done();
 293
 294        /* Update configuration for the clock setting */
 295        if (!lp55xx_is_extclk_used(chip))
 296                cfg |= LP5562_CLK_INT;
 297
 298        ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg);
 299        if (ret)
 300                return ret;
 301
 302        /* Initialize all channels PWM to zero -> leds off */
 303        lp55xx_write(chip, LP5562_REG_R_PWM, 0);
 304        lp55xx_write(chip, LP5562_REG_G_PWM, 0);
 305        lp55xx_write(chip, LP5562_REG_B_PWM, 0);
 306        lp55xx_write(chip, LP5562_REG_W_PWM, 0);
 307
 308        /* Set LED map as register PWM by default */
 309        lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
 310
 311        return 0;
 312}
 313
 314static void lp5562_led_brightness_work(struct work_struct *work)
 315{
 316        struct lp55xx_led *led = container_of(work, struct lp55xx_led,
 317                                              brightness_work);
 318        struct lp55xx_chip *chip = led->chip;
 319        u8 addr[] = {
 320                LP5562_REG_R_PWM,
 321                LP5562_REG_G_PWM,
 322                LP5562_REG_B_PWM,
 323                LP5562_REG_W_PWM,
 324        };
 325
 326        mutex_lock(&chip->lock);
 327        lp55xx_write(chip, addr[led->chan_nr], led->brightness);
 328        mutex_unlock(&chip->lock);
 329}
 330
 331static void lp5562_write_program_memory(struct lp55xx_chip *chip,
 332                                        u8 base, const u8 *rgb, int size)
 333{
 334        int i;
 335
 336        if (!rgb || size <= 0)
 337                return;
 338
 339        for (i = 0; i < size; i++)
 340                lp55xx_write(chip, base + i, *(rgb + i));
 341
 342        lp55xx_write(chip, base + i, 0);
 343        lp55xx_write(chip, base + i + 1, 0);
 344}
 345
 346/* check the size of program count */
 347static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
 348{
 349        return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
 350                ptn->size_g >= LP5562_PROGRAM_LENGTH ||
 351                ptn->size_b >= LP5562_PROGRAM_LENGTH);
 352}
 353
 354static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
 355{
 356        struct lp55xx_predef_pattern *ptn;
 357        int i;
 358
 359        if (mode == LP5562_PATTERN_OFF) {
 360                lp5562_run_engine(chip, false);
 361                return 0;
 362        }
 363
 364        ptn = chip->pdata->patterns + (mode - 1);
 365        if (!ptn || _is_pc_overflow(ptn)) {
 366                dev_err(&chip->cl->dev, "invalid pattern data\n");
 367                return -EINVAL;
 368        }
 369
 370        lp5562_stop_engine(chip);
 371
 372        /* Set LED map as RGB */
 373        lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
 374
 375        /* Load engines */
 376        for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
 377                chip->engine_idx = i;
 378                lp5562_load_engine(chip);
 379        }
 380
 381        /* Clear program registers */
 382        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
 383        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
 384        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
 385        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
 386        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
 387        lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
 388
 389        /* Program engines */
 390        lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
 391                                ptn->r, ptn->size_r);
 392        lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
 393                                ptn->g, ptn->size_g);
 394        lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
 395                                ptn->b, ptn->size_b);
 396
 397        /* Run engines */
 398        lp5562_run_engine(chip, true);
 399
 400        return 0;
 401}
 402
 403static ssize_t lp5562_store_pattern(struct device *dev,
 404                                struct device_attribute *attr,
 405                                const char *buf, size_t len)
 406{
 407        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 408        struct lp55xx_chip *chip = led->chip;
 409        struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
 410        int num_patterns = chip->pdata->num_patterns;
 411        unsigned long mode;
 412        int ret;
 413
 414        ret = kstrtoul(buf, 0, &mode);
 415        if (ret)
 416                return ret;
 417
 418        if (mode > num_patterns || !ptn)
 419                return -EINVAL;
 420
 421        mutex_lock(&chip->lock);
 422        ret = lp5562_run_predef_led_pattern(chip, mode);
 423        mutex_unlock(&chip->lock);
 424
 425        if (ret)
 426                return ret;
 427
 428        return len;
 429}
 430
 431static ssize_t lp5562_store_engine_mux(struct device *dev,
 432                                     struct device_attribute *attr,
 433                                     const char *buf, size_t len)
 434{
 435        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 436        struct lp55xx_chip *chip = led->chip;
 437        u8 mask;
 438        u8 val;
 439
 440        /* LED map
 441         * R ... Engine 1 (fixed)
 442         * G ... Engine 2 (fixed)
 443         * B ... Engine 3 (fixed)
 444         * W ... Engine 1 or 2 or 3
 445         */
 446
 447        if (sysfs_streq(buf, "RGB")) {
 448                mask = LP5562_ENG_FOR_RGB_M;
 449                val = LP5562_ENG_SEL_RGB;
 450        } else if (sysfs_streq(buf, "W")) {
 451                enum lp55xx_engine_index idx = chip->engine_idx;
 452
 453                mask = LP5562_ENG_FOR_W_M;
 454                switch (idx) {
 455                case LP55XX_ENGINE_1:
 456                        val = LP5562_ENG1_FOR_W;
 457                        break;
 458                case LP55XX_ENGINE_2:
 459                        val = LP5562_ENG2_FOR_W;
 460                        break;
 461                case LP55XX_ENGINE_3:
 462                        val = LP5562_ENG3_FOR_W;
 463                        break;
 464                default:
 465                        return -EINVAL;
 466                }
 467
 468        } else {
 469                dev_err(dev, "choose RGB or W\n");
 470                return -EINVAL;
 471        }
 472
 473        mutex_lock(&chip->lock);
 474        lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
 475        mutex_unlock(&chip->lock);
 476
 477        return len;
 478}
 479
 480static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
 481static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
 482
 483static struct attribute *lp5562_attributes[] = {
 484        &dev_attr_led_pattern.attr,
 485        &dev_attr_engine_mux.attr,
 486        NULL,
 487};
 488
 489static const struct attribute_group lp5562_group = {
 490        .attrs = lp5562_attributes,
 491};
 492
 493/* Chip specific configurations */
 494static struct lp55xx_device_config lp5562_cfg = {
 495        .max_channel  = LP5562_MAX_LEDS,
 496        .reset = {
 497                .addr = LP5562_REG_RESET,
 498                .val  = LP5562_RESET,
 499        },
 500        .enable = {
 501                .addr = LP5562_REG_ENABLE,
 502                .val  = LP5562_ENABLE_DEFAULT,
 503        },
 504        .post_init_device   = lp5562_post_init_device,
 505        .set_led_current    = lp5562_set_led_current,
 506        .brightness_work_fn = lp5562_led_brightness_work,
 507        .run_engine         = lp5562_run_engine,
 508        .firmware_cb        = lp5562_firmware_loaded,
 509        .dev_attr_group     = &lp5562_group,
 510};
 511
 512static int lp5562_probe(struct i2c_client *client,
 513                        const struct i2c_device_id *id)
 514{
 515        int ret;
 516        struct lp55xx_chip *chip;
 517        struct lp55xx_led *led;
 518        struct lp55xx_platform_data *pdata;
 519        struct device_node *np = client->dev.of_node;
 520
 521        if (!client->dev.platform_data) {
 522                if (np) {
 523                        ret = lp55xx_of_populate_pdata(&client->dev, np);
 524                        if (ret < 0)
 525                                return ret;
 526                } else {
 527                        dev_err(&client->dev, "no platform data\n");
 528                        return -EINVAL;
 529                }
 530        }
 531        pdata = client->dev.platform_data;
 532
 533        chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 534        if (!chip)
 535                return -ENOMEM;
 536
 537        led = devm_kzalloc(&client->dev,
 538                        sizeof(*led) * pdata->num_channels, GFP_KERNEL);
 539        if (!led)
 540                return -ENOMEM;
 541
 542        chip->cl = client;
 543        chip->pdata = pdata;
 544        chip->cfg = &lp5562_cfg;
 545
 546        mutex_init(&chip->lock);
 547
 548        i2c_set_clientdata(client, led);
 549
 550        ret = lp55xx_init_device(chip);
 551        if (ret)
 552                goto err_init;
 553
 554        ret = lp55xx_register_leds(led, chip);
 555        if (ret)
 556                goto err_register_leds;
 557
 558        ret = lp55xx_register_sysfs(chip);
 559        if (ret) {
 560                dev_err(&client->dev, "registering sysfs failed\n");
 561                goto err_register_sysfs;
 562        }
 563
 564        return 0;
 565
 566err_register_sysfs:
 567        lp55xx_unregister_leds(led, chip);
 568err_register_leds:
 569        lp55xx_deinit_device(chip);
 570err_init:
 571        return ret;
 572}
 573
 574static int lp5562_remove(struct i2c_client *client)
 575{
 576        struct lp55xx_led *led = i2c_get_clientdata(client);
 577        struct lp55xx_chip *chip = led->chip;
 578
 579        lp5562_stop_engine(chip);
 580
 581        lp55xx_unregister_sysfs(chip);
 582        lp55xx_unregister_leds(led, chip);
 583        lp55xx_deinit_device(chip);
 584
 585        return 0;
 586}
 587
 588static const struct i2c_device_id lp5562_id[] = {
 589        { "lp5562", 0 },
 590        { }
 591};
 592MODULE_DEVICE_TABLE(i2c, lp5562_id);
 593
 594#ifdef CONFIG_OF
 595static const struct of_device_id of_lp5562_leds_match[] = {
 596        { .compatible = "ti,lp5562", },
 597        {},
 598};
 599
 600MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
 601#endif
 602
 603static struct i2c_driver lp5562_driver = {
 604        .driver = {
 605                .name   = "lp5562",
 606                .of_match_table = of_match_ptr(of_lp5562_leds_match),
 607        },
 608        .probe          = lp5562_probe,
 609        .remove         = lp5562_remove,
 610        .id_table       = lp5562_id,
 611};
 612
 613module_i2c_driver(lp5562_driver);
 614
 615MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
 616MODULE_AUTHOR("Milo Kim");
 617MODULE_LICENSE("GPL");
 618
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.