linux/drivers/mfd/88pm800.c
<<
>>
Prefs
   1/*
   2 * Base driver for Marvell 88PM800
   3 *
   4 * Copyright (C) 2012 Marvell International Ltd.
   5 * Haojian Zhuang <haojian.zhuang@marvell.com>
   6 * Joseph(Yossi) Hanin <yhanin@marvell.com>
   7 * Qiao Zhou <zhouqiao@marvell.com>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General
  10 * Public License. See the file "COPYING" in the main directory of this
  11 * archive for more details.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21 */
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/i2c.h>
  26#include <linux/mfd/core.h>
  27#include <linux/mfd/88pm80x.h>
  28#include <linux/slab.h>
  29
  30#define PM800_CHIP_ID                   (0x00)
  31
  32/* Interrupt Registers */
  33#define PM800_INT_STATUS1               (0x05)
  34#define PM800_ONKEY_INT_STS1            (1 << 0)
  35#define PM800_EXTON_INT_STS1            (1 << 1)
  36#define PM800_CHG_INT_STS1                      (1 << 2)
  37#define PM800_BAT_INT_STS1                      (1 << 3)
  38#define PM800_RTC_INT_STS1                      (1 << 4)
  39#define PM800_CLASSD_OC_INT_STS1        (1 << 5)
  40
  41#define PM800_INT_STATUS2               (0x06)
  42#define PM800_VBAT_INT_STS2             (1 << 0)
  43#define PM800_VSYS_INT_STS2             (1 << 1)
  44#define PM800_VCHG_INT_STS2             (1 << 2)
  45#define PM800_TINT_INT_STS2             (1 << 3)
  46#define PM800_GPADC0_INT_STS2   (1 << 4)
  47#define PM800_TBAT_INT_STS2             (1 << 5)
  48#define PM800_GPADC2_INT_STS2   (1 << 6)
  49#define PM800_GPADC3_INT_STS2   (1 << 7)
  50
  51#define PM800_INT_STATUS3               (0x07)
  52
  53#define PM800_INT_STATUS4               (0x08)
  54#define PM800_GPIO0_INT_STS4            (1 << 0)
  55#define PM800_GPIO1_INT_STS4            (1 << 1)
  56#define PM800_GPIO2_INT_STS4            (1 << 2)
  57#define PM800_GPIO3_INT_STS4            (1 << 3)
  58#define PM800_GPIO4_INT_STS4            (1 << 4)
  59
  60#define PM800_INT_ENA_1         (0x09)
  61#define PM800_ONKEY_INT_ENA1            (1 << 0)
  62#define PM800_EXTON_INT_ENA1            (1 << 1)
  63#define PM800_CHG_INT_ENA1                      (1 << 2)
  64#define PM800_BAT_INT_ENA1                      (1 << 3)
  65#define PM800_RTC_INT_ENA1                      (1 << 4)
  66#define PM800_CLASSD_OC_INT_ENA1        (1 << 5)
  67
  68#define PM800_INT_ENA_2         (0x0A)
  69#define PM800_VBAT_INT_ENA2             (1 << 0)
  70#define PM800_VSYS_INT_ENA2             (1 << 1)
  71#define PM800_VCHG_INT_ENA2             (1 << 2)
  72#define PM800_TINT_INT_ENA2             (1 << 3)
  73
  74#define PM800_INT_ENA_3         (0x0B)
  75#define PM800_GPADC0_INT_ENA3           (1 << 0)
  76#define PM800_GPADC1_INT_ENA3           (1 << 1)
  77#define PM800_GPADC2_INT_ENA3           (1 << 2)
  78#define PM800_GPADC3_INT_ENA3           (1 << 3)
  79#define PM800_GPADC4_INT_ENA3           (1 << 4)
  80
  81#define PM800_INT_ENA_4         (0x0C)
  82#define PM800_GPIO0_INT_ENA4            (1 << 0)
  83#define PM800_GPIO1_INT_ENA4            (1 << 1)
  84#define PM800_GPIO2_INT_ENA4            (1 << 2)
  85#define PM800_GPIO3_INT_ENA4            (1 << 3)
  86#define PM800_GPIO4_INT_ENA4            (1 << 4)
  87
  88/* number of INT_ENA & INT_STATUS regs */
  89#define PM800_INT_REG_NUM                       (4)
  90
  91/* Interrupt Number in 88PM800 */
  92enum {
  93        PM800_IRQ_ONKEY,        /*EN1b0 *//*0 */
  94        PM800_IRQ_EXTON,        /*EN1b1 */
  95        PM800_IRQ_CHG,          /*EN1b2 */
  96        PM800_IRQ_BAT,          /*EN1b3 */
  97        PM800_IRQ_RTC,          /*EN1b4 */
  98        PM800_IRQ_CLASSD,       /*EN1b5 *//*5 */
  99        PM800_IRQ_VBAT,         /*EN2b0 */
 100        PM800_IRQ_VSYS,         /*EN2b1 */
 101        PM800_IRQ_VCHG,         /*EN2b2 */
 102        PM800_IRQ_TINT,         /*EN2b3 */
 103        PM800_IRQ_GPADC0,       /*EN3b0 *//*10 */
 104        PM800_IRQ_GPADC1,       /*EN3b1 */
 105        PM800_IRQ_GPADC2,       /*EN3b2 */
 106        PM800_IRQ_GPADC3,       /*EN3b3 */
 107        PM800_IRQ_GPADC4,       /*EN3b4 */
 108        PM800_IRQ_GPIO0,        /*EN4b0 *//*15 */
 109        PM800_IRQ_GPIO1,        /*EN4b1 */
 110        PM800_IRQ_GPIO2,        /*EN4b2 */
 111        PM800_IRQ_GPIO3,        /*EN4b3 */
 112        PM800_IRQ_GPIO4,        /*EN4b4 *//*19 */
 113        PM800_MAX_IRQ,
 114};
 115
 116enum {
 117        /* Procida */
 118        PM800_CHIP_A0  = 0x60,
 119        PM800_CHIP_A1  = 0x61,
 120        PM800_CHIP_B0  = 0x62,
 121        PM800_CHIP_C0  = 0x63,
 122        PM800_CHIP_END = PM800_CHIP_C0,
 123
 124        /* Make sure to update this to the last stepping */
 125        PM8XXX_CHIP_END = PM800_CHIP_END
 126};
 127
 128static const struct i2c_device_id pm80x_id_table[] = {
 129        {"88PM800", CHIP_PM800},
 130        {} /* NULL terminated */
 131};
 132MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
 133
 134static struct resource rtc_resources[] = {
 135        {
 136         .name = "88pm80x-rtc",
 137         .start = PM800_IRQ_RTC,
 138         .end = PM800_IRQ_RTC,
 139         .flags = IORESOURCE_IRQ,
 140         },
 141};
 142
 143static struct mfd_cell rtc_devs[] = {
 144        {
 145         .name = "88pm80x-rtc",
 146         .num_resources = ARRAY_SIZE(rtc_resources),
 147         .resources = &rtc_resources[0],
 148         .id = -1,
 149         },
 150};
 151
 152static struct resource onkey_resources[] = {
 153        {
 154         .name = "88pm80x-onkey",
 155         .start = PM800_IRQ_ONKEY,
 156         .end = PM800_IRQ_ONKEY,
 157         .flags = IORESOURCE_IRQ,
 158         },
 159};
 160
 161static struct mfd_cell onkey_devs[] = {
 162        {
 163         .name = "88pm80x-onkey",
 164         .num_resources = 1,
 165         .resources = &onkey_resources[0],
 166         .id = -1,
 167         },
 168};
 169
 170static const struct regmap_irq pm800_irqs[] = {
 171        /* INT0 */
 172        [PM800_IRQ_ONKEY] = {
 173                .mask = PM800_ONKEY_INT_ENA1,
 174        },
 175        [PM800_IRQ_EXTON] = {
 176                .mask = PM800_EXTON_INT_ENA1,
 177        },
 178        [PM800_IRQ_CHG] = {
 179                .mask = PM800_CHG_INT_ENA1,
 180        },
 181        [PM800_IRQ_BAT] = {
 182                .mask = PM800_BAT_INT_ENA1,
 183        },
 184        [PM800_IRQ_RTC] = {
 185                .mask = PM800_RTC_INT_ENA1,
 186        },
 187        [PM800_IRQ_CLASSD] = {
 188                .mask = PM800_CLASSD_OC_INT_ENA1,
 189        },
 190        /* INT1 */
 191        [PM800_IRQ_VBAT] = {
 192                .reg_offset = 1,
 193                .mask = PM800_VBAT_INT_ENA2,
 194        },
 195        [PM800_IRQ_VSYS] = {
 196                .reg_offset = 1,
 197                .mask = PM800_VSYS_INT_ENA2,
 198        },
 199        [PM800_IRQ_VCHG] = {
 200                .reg_offset = 1,
 201                .mask = PM800_VCHG_INT_ENA2,
 202        },
 203        [PM800_IRQ_TINT] = {
 204                .reg_offset = 1,
 205                .mask = PM800_TINT_INT_ENA2,
 206        },
 207        /* INT2 */
 208        [PM800_IRQ_GPADC0] = {
 209                .reg_offset = 2,
 210                .mask = PM800_GPADC0_INT_ENA3,
 211        },
 212        [PM800_IRQ_GPADC1] = {
 213                .reg_offset = 2,
 214                .mask = PM800_GPADC1_INT_ENA3,
 215        },
 216        [PM800_IRQ_GPADC2] = {
 217                .reg_offset = 2,
 218                .mask = PM800_GPADC2_INT_ENA3,
 219        },
 220        [PM800_IRQ_GPADC3] = {
 221                .reg_offset = 2,
 222                .mask = PM800_GPADC3_INT_ENA3,
 223        },
 224        [PM800_IRQ_GPADC4] = {
 225                .reg_offset = 2,
 226                .mask = PM800_GPADC4_INT_ENA3,
 227        },
 228        /* INT3 */
 229        [PM800_IRQ_GPIO0] = {
 230                .reg_offset = 3,
 231                .mask = PM800_GPIO0_INT_ENA4,
 232        },
 233        [PM800_IRQ_GPIO1] = {
 234                .reg_offset = 3,
 235                .mask = PM800_GPIO1_INT_ENA4,
 236        },
 237        [PM800_IRQ_GPIO2] = {
 238                .reg_offset = 3,
 239                .mask = PM800_GPIO2_INT_ENA4,
 240        },
 241        [PM800_IRQ_GPIO3] = {
 242                .reg_offset = 3,
 243                .mask = PM800_GPIO3_INT_ENA4,
 244        },
 245        [PM800_IRQ_GPIO4] = {
 246                .reg_offset = 3,
 247                .mask = PM800_GPIO4_INT_ENA4,
 248        },
 249};
 250
 251static int __devinit device_gpadc_init(struct pm80x_chip *chip,
 252                                       struct pm80x_platform_data *pdata)
 253{
 254        struct pm80x_subchip *subchip = chip->subchip;
 255        struct regmap *map = subchip->regmap_gpadc;
 256        int data = 0, mask = 0, ret = 0;
 257
 258        if (!map) {
 259                dev_warn(chip->dev,
 260                         "Warning: gpadc regmap is not available!\n");
 261                return -EINVAL;
 262        }
 263        /*
 264         * initialize GPADC without activating it turn on GPADC
 265         * measurments
 266         */
 267        ret = regmap_update_bits(map,
 268                                 PM800_GPADC_MISC_CONFIG2,
 269                                 PM800_GPADC_MISC_GPFSM_EN,
 270                                 PM800_GPADC_MISC_GPFSM_EN);
 271        if (ret < 0)
 272                goto out;
 273        /*
 274         * This function configures the ADC as requires for
 275         * CP implementation.CP does not "own" the ADC configuration
 276         * registers and relies on AP.
 277         * Reason: enable automatic ADC measurements needed
 278         * for CP to get VBAT and RF temperature readings.
 279         */
 280        ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
 281                                 PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
 282        if (ret < 0)
 283                goto out;
 284        ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
 285                                 (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
 286                                 (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
 287        if (ret < 0)
 288                goto out;
 289
 290        /*
 291         * the defult of PM800 is GPADC operates at 100Ks/s rate
 292         * and Number of GPADC slots with active current bias prior
 293         * to GPADC sampling = 1 slot for all GPADCs set for
 294         * Temprature mesurmants
 295         */
 296        mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
 297                PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
 298
 299        if (pdata && (pdata->batt_det == 0))
 300                data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
 301                        PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
 302        else
 303                data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
 304                        PM800_GPADC_GP_BIAS_EN3);
 305
 306        ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
 307        if (ret < 0)
 308                goto out;
 309
 310        dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
 311        return 0;
 312
 313out:
 314        dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
 315        return ret;
 316}
 317
 318static int __devinit device_irq_init_800(struct pm80x_chip *chip)
 319{
 320        struct regmap *map = chip->regmap;
 321        unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
 322        int data, mask, ret = -EINVAL;
 323
 324        if (!map || !chip->irq) {
 325                dev_err(chip->dev, "incorrect parameters\n");
 326                return -EINVAL;
 327        }
 328
 329        /*
 330         * irq_mode defines the way of clearing interrupt. it's read-clear by
 331         * default.
 332         */
 333        mask =
 334            PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
 335            PM800_WAKEUP2_INT_MASK;
 336
 337        data = PM800_WAKEUP2_INT_CLEAR;
 338        ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
 339
 340        if (ret < 0)
 341                goto out;
 342
 343        ret =
 344            regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
 345                                chip->regmap_irq_chip, &chip->irq_data);
 346
 347out:
 348        return ret;
 349}
 350
 351static void device_irq_exit_800(struct pm80x_chip *chip)
 352{
 353        regmap_del_irq_chip(chip->irq, chip->irq_data);
 354}
 355
 356static struct regmap_irq_chip pm800_irq_chip = {
 357        .name = "88pm800",
 358        .irqs = pm800_irqs,
 359        .num_irqs = ARRAY_SIZE(pm800_irqs),
 360
 361        .num_regs = 4,
 362        .status_base = PM800_INT_STATUS1,
 363        .mask_base = PM800_INT_ENA_1,
 364        .ack_base = PM800_INT_STATUS1,
 365};
 366
 367static int pm800_pages_init(struct pm80x_chip *chip)
 368{
 369        struct pm80x_subchip *subchip;
 370        struct i2c_client *client = chip->client;
 371
 372        subchip = chip->subchip;
 373        /* PM800 block power: i2c addr 0x31 */
 374        if (subchip->power_page_addr) {
 375                subchip->power_page =
 376                    i2c_new_dummy(client->adapter, subchip->power_page_addr);
 377                subchip->regmap_power =
 378                    devm_regmap_init_i2c(subchip->power_page,
 379                                         &pm80x_regmap_config);
 380                i2c_set_clientdata(subchip->power_page, chip);
 381        } else
 382                dev_info(chip->dev,
 383                         "PM800 block power 0x31: No power_page_addr\n");
 384
 385        /* PM800 block GPADC: i2c addr 0x32 */
 386        if (subchip->gpadc_page_addr) {
 387                subchip->gpadc_page = i2c_new_dummy(client->adapter,
 388                                                    subchip->gpadc_page_addr);
 389                subchip->regmap_gpadc =
 390                    devm_regmap_init_i2c(subchip->gpadc_page,
 391                                         &pm80x_regmap_config);
 392                i2c_set_clientdata(subchip->gpadc_page, chip);
 393        } else
 394                dev_info(chip->dev,
 395                         "PM800 block GPADC 0x32: No gpadc_page_addr\n");
 396
 397        return 0;
 398}
 399
 400static void pm800_pages_exit(struct pm80x_chip *chip)
 401{
 402        struct pm80x_subchip *subchip;
 403
 404        regmap_exit(chip->regmap);
 405        i2c_unregister_device(chip->client);
 406
 407        subchip = chip->subchip;
 408        if (subchip->power_page) {
 409                regmap_exit(subchip->regmap_power);
 410                i2c_unregister_device(subchip->power_page);
 411        }
 412        if (subchip->gpadc_page) {
 413                regmap_exit(subchip->regmap_gpadc);
 414                i2c_unregister_device(subchip->gpadc_page);
 415        }
 416}
 417
 418static int __devinit device_800_init(struct pm80x_chip *chip,
 419                                     struct pm80x_platform_data *pdata)
 420{
 421        int ret, pmic_id;
 422        unsigned int val;
 423
 424        ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val);
 425        if (ret < 0) {
 426                dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
 427                goto out;
 428        }
 429
 430        pmic_id = val & PM80X_VERSION_MASK;
 431
 432        if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) {
 433                chip->version = val;
 434                dev_info(chip->dev,
 435                         "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val);
 436        } else {
 437                dev_err(chip->dev,
 438                        "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
 439                ret = -EINVAL;
 440                goto out;
 441        }
 442
 443        /*
 444         * alarm wake up bit will be clear in device_irq_init(),
 445         * read before that
 446         */
 447        ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
 448        if (ret < 0) {
 449                dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
 450                goto out;
 451        }
 452        if (val & PM800_ALARM_WAKEUP) {
 453                if (pdata && pdata->rtc)
 454                        pdata->rtc->rtc_wakeup = 1;
 455        }
 456
 457        ret = device_gpadc_init(chip, pdata);
 458        if (ret < 0) {
 459                dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
 460                goto out;
 461        }
 462
 463        chip->regmap_irq_chip = &pm800_irq_chip;
 464
 465        ret = device_irq_init_800(chip);
 466        if (ret < 0) {
 467                dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
 468                goto out;
 469        }
 470
 471        ret =
 472            mfd_add_devices(chip->dev, 0, &onkey_devs[0],
 473                            ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
 474                            NULL);
 475        if (ret < 0) {
 476                dev_err(chip->dev, "Failed to add onkey subdev\n");
 477                goto out_dev;
 478        } else
 479                dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__);
 480
 481        if (pdata && pdata->rtc) {
 482                rtc_devs[0].platform_data = pdata->rtc;
 483                rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
 484                ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
 485                                      ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
 486                if (ret < 0) {
 487                        dev_err(chip->dev, "Failed to add rtc subdev\n");
 488                        goto out_dev;
 489                } else
 490                        dev_info(chip->dev,
 491                                 "[%s]:Added mfd rtc_devs\n", __func__);
 492        }
 493
 494        return 0;
 495out_dev:
 496        mfd_remove_devices(chip->dev);
 497        device_irq_exit_800(chip);
 498out:
 499        return ret;
 500}
 501
 502static int __devinit pm800_probe(struct i2c_client *client,
 503                                 const struct i2c_device_id *id)
 504{
 505        int ret = 0;
 506        struct pm80x_chip *chip;
 507        struct pm80x_platform_data *pdata = client->dev.platform_data;
 508        struct pm80x_subchip *subchip;
 509
 510        ret = pm80x_init(client, id);
 511        if (ret) {
 512                dev_err(&client->dev, "pm800_init fail\n");
 513                goto out_init;
 514        }
 515
 516        chip = i2c_get_clientdata(client);
 517
 518        /* init subchip for PM800 */
 519        subchip =
 520            devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
 521                         GFP_KERNEL);
 522        if (!subchip) {
 523                ret = -ENOMEM;
 524                goto err_subchip_alloc;
 525        }
 526
 527        subchip->power_page_addr = pdata->power_page_addr;
 528        subchip->gpadc_page_addr = pdata->gpadc_page_addr;
 529        chip->subchip = subchip;
 530
 531        ret = device_800_init(chip, pdata);
 532        if (ret) {
 533                dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
 534                goto err_800_init;
 535        }
 536
 537        ret = pm800_pages_init(chip);
 538        if (ret) {
 539                dev_err(&client->dev, "pm800_pages_init failed!\n");
 540                goto err_page_init;
 541        }
 542
 543        if (pdata->plat_config)
 544                pdata->plat_config(chip, pdata);
 545
 546err_page_init:
 547        mfd_remove_devices(chip->dev);
 548        device_irq_exit_800(chip);
 549err_800_init:
 550        devm_kfree(&client->dev, subchip);
 551err_subchip_alloc:
 552        pm80x_deinit(client);
 553out_init:
 554        return ret;
 555}
 556
 557static int __devexit pm800_remove(struct i2c_client *client)
 558{
 559        struct pm80x_chip *chip = i2c_get_clientdata(client);
 560
 561        mfd_remove_devices(chip->dev);
 562        device_irq_exit_800(chip);
 563
 564        pm800_pages_exit(chip);
 565        devm_kfree(&client->dev, chip->subchip);
 566
 567        pm80x_deinit(client);
 568
 569        return 0;
 570}
 571
 572static struct i2c_driver pm800_driver = {
 573        .driver = {
 574                .name = "88PM80X",
 575                .owner = THIS_MODULE,
 576                .pm = &pm80x_pm_ops,
 577                },
 578        .probe = pm800_probe,
 579        .remove = __devexit_p(pm800_remove),
 580        .id_table = pm80x_id_table,
 581};
 582
 583static int __init pm800_i2c_init(void)
 584{
 585        return i2c_add_driver(&pm800_driver);
 586}
 587subsys_initcall(pm800_i2c_init);
 588
 589static void __exit pm800_i2c_exit(void)
 590{
 591        i2c_del_driver(&pm800_driver);
 592}
 593module_exit(pm800_i2c_exit);
 594
 595MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
 596MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
 597MODULE_LICENSE("GPL");
 598
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.