linux/drivers/i2c/busses/i2c-cht-wc.c
<<
>>
Prefs
   1/*
   2 * Intel CHT Whiskey Cove PMIC I2C Master driver
   3 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
   4 *
   5 * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
   6 * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License version
  10 * 2 as published by the Free Software Foundation, or (at your option)
  11 * any later version.
  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
  19#include <linux/acpi.h>
  20#include <linux/completion.h>
  21#include <linux/delay.h>
  22#include <linux/i2c.h>
  23#include <linux/interrupt.h>
  24#include <linux/irq.h>
  25#include <linux/irqdomain.h>
  26#include <linux/mfd/intel_soc_pmic.h>
  27#include <linux/module.h>
  28#include <linux/platform_device.h>
  29#include <linux/power/bq24190_charger.h>
  30#include <linux/slab.h>
  31
  32#define CHT_WC_I2C_CTRL                 0x5e24
  33#define CHT_WC_I2C_CTRL_WR              BIT(0)
  34#define CHT_WC_I2C_CTRL_RD              BIT(1)
  35#define CHT_WC_I2C_CLIENT_ADDR          0x5e25
  36#define CHT_WC_I2C_REG_OFFSET           0x5e26
  37#define CHT_WC_I2C_WRDATA               0x5e27
  38#define CHT_WC_I2C_RDDATA               0x5e28
  39
  40#define CHT_WC_EXTCHGRIRQ               0x6e0a
  41#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ    BIT(0)
  42#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ     BIT(1)
  43#define CHT_WC_EXTCHGRIRQ_READ_IRQ      BIT(2)
  44#define CHT_WC_EXTCHGRIRQ_NACK_IRQ      BIT(3)
  45#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK  ((u8)GENMASK(3, 1))
  46#define CHT_WC_EXTCHGRIRQ_MSK           0x6e17
  47
  48struct cht_wc_i2c_adap {
  49        struct i2c_adapter adapter;
  50        wait_queue_head_t wait;
  51        struct irq_chip irqchip;
  52        struct mutex adap_lock;
  53        struct mutex irqchip_lock;
  54        struct regmap *regmap;
  55        struct irq_domain *irq_domain;
  56        struct i2c_client *client;
  57        int client_irq;
  58        u8 irq_mask;
  59        u8 old_irq_mask;
  60        int read_data;
  61        bool io_error;
  62        bool done;
  63};
  64
  65static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data)
  66{
  67        struct cht_wc_i2c_adap *adap = data;
  68        int ret, reg;
  69
  70        mutex_lock(&adap->adap_lock);
  71
  72        /* Read IRQs */
  73        ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, &reg);
  74        if (ret) {
  75                dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n");
  76                mutex_unlock(&adap->adap_lock);
  77                return IRQ_NONE;
  78        }
  79
  80        reg &= ~adap->irq_mask;
  81
  82        /* Reads must be acked after reading the received data. */
  83        ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data);
  84        if (ret)
  85                adap->io_error = true;
  86
  87        /*
  88         * Immediately ack IRQs, so that if new IRQs arrives while we're
  89         * handling the previous ones our irq will re-trigger when we're done.
  90         */
  91        ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg);
  92        if (ret)
  93                dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n");
  94
  95        if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) {
  96                adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ);
  97                adap->done = true;
  98        }
  99
 100        mutex_unlock(&adap->adap_lock);
 101
 102        if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK)
 103                wake_up(&adap->wait);
 104
 105        /*
 106         * Do NOT use handle_nested_irq here, the client irq handler will
 107         * likely want to do i2c transfers and the i2c controller uses this
 108         * interrupt handler as well, so running the client irq handler from
 109         * this thread will cause things to lock up.
 110         */
 111        if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) {
 112                /*
 113                 * generic_handle_irq expects local IRQs to be disabled
 114                 * as normally it is called from interrupt context.
 115                 */
 116                local_irq_disable();
 117                generic_handle_irq(adap->client_irq);
 118                local_irq_enable();
 119        }
 120
 121        return IRQ_HANDLED;
 122}
 123
 124static u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap)
 125{
 126        /* This i2c adapter only supports SMBUS byte transfers */
 127        return I2C_FUNC_SMBUS_BYTE_DATA;
 128}
 129
 130static int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr,
 131                                      unsigned short flags, char read_write,
 132                                      u8 command, int size,
 133                                      union i2c_smbus_data *data)
 134{
 135        struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap);
 136        int ret;
 137
 138        mutex_lock(&adap->adap_lock);
 139        adap->io_error = false;
 140        adap->done = false;
 141        mutex_unlock(&adap->adap_lock);
 142
 143        ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr);
 144        if (ret)
 145                return ret;
 146
 147        if (read_write == I2C_SMBUS_WRITE) {
 148                ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte);
 149                if (ret)
 150                        return ret;
 151        }
 152
 153        ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command);
 154        if (ret)
 155                return ret;
 156
 157        ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL,
 158                           (read_write == I2C_SMBUS_WRITE) ?
 159                           CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD);
 160        if (ret)
 161                return ret;
 162
 163        ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30));
 164        if (ret == 0) {
 165                /*
 166                 * The CHT GPIO controller serializes all IRQs, sometimes
 167                 * causing significant delays, check status manually.
 168                 */
 169                cht_wc_i2c_adap_thread_handler(0, adap);
 170                if (!adap->done)
 171                        return -ETIMEDOUT;
 172        }
 173
 174        ret = 0;
 175        mutex_lock(&adap->adap_lock);
 176        if (adap->io_error)
 177                ret = -EIO;
 178        else if (read_write == I2C_SMBUS_READ)
 179                data->byte = adap->read_data;
 180        mutex_unlock(&adap->adap_lock);
 181
 182        return ret;
 183}
 184
 185static const struct i2c_algorithm cht_wc_i2c_adap_algo = {
 186        .functionality = cht_wc_i2c_adap_master_func,
 187        .smbus_xfer = cht_wc_i2c_adap_smbus_xfer,
 188};
 189
 190/**** irqchip for the client connected to the extchgr i2c adapter ****/
 191static void cht_wc_i2c_irq_lock(struct irq_data *data)
 192{
 193        struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
 194
 195        mutex_lock(&adap->irqchip_lock);
 196}
 197
 198static void cht_wc_i2c_irq_sync_unlock(struct irq_data *data)
 199{
 200        struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
 201        int ret;
 202
 203        if (adap->irq_mask != adap->old_irq_mask) {
 204                ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK,
 205                                   adap->irq_mask);
 206                if (ret == 0)
 207                        adap->old_irq_mask = adap->irq_mask;
 208                else
 209                        dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n");
 210        }
 211
 212        mutex_unlock(&adap->irqchip_lock);
 213}
 214
 215static void cht_wc_i2c_irq_enable(struct irq_data *data)
 216{
 217        struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
 218
 219        adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ;
 220}
 221
 222static void cht_wc_i2c_irq_disable(struct irq_data *data)
 223{
 224        struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
 225
 226        adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ;
 227}
 228
 229static const struct irq_chip cht_wc_i2c_irq_chip = {
 230        .irq_bus_lock           = cht_wc_i2c_irq_lock,
 231        .irq_bus_sync_unlock    = cht_wc_i2c_irq_sync_unlock,
 232        .irq_disable            = cht_wc_i2c_irq_disable,
 233        .irq_enable             = cht_wc_i2c_irq_enable,
 234        .name                   = "cht_wc_ext_chrg_irq_chip",
 235};
 236
 237static const char * const bq24190_suppliers[] = {
 238        "tcpm-source-psy-i2c-fusb302" };
 239
 240static const struct property_entry bq24190_props[] = {
 241        PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_suppliers),
 242        PROPERTY_ENTRY_BOOL("omit-battery-class"),
 243        PROPERTY_ENTRY_BOOL("disable-reset"),
 244        { }
 245};
 246
 247static struct regulator_consumer_supply fusb302_consumer = {
 248        .supply = "vbus",
 249        /* Must match fusb302 dev_name in intel_cht_int33fe.c */
 250        .dev_name = "i2c-fusb302",
 251};
 252
 253static const struct regulator_init_data bq24190_vbus_init_data = {
 254        .constraints = {
 255                /* The name is used in intel_cht_int33fe.c do not change. */
 256                .name = "cht_wc_usb_typec_vbus",
 257                .valid_ops_mask = REGULATOR_CHANGE_STATUS,
 258        },
 259        .consumer_supplies = &fusb302_consumer,
 260        .num_consumer_supplies = 1,
 261};
 262
 263static struct bq24190_platform_data bq24190_pdata = {
 264        .regulator_init_data = &bq24190_vbus_init_data,
 265};
 266
 267static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
 268{
 269        struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
 270        struct cht_wc_i2c_adap *adap;
 271        struct i2c_board_info board_info = {
 272                .type = "bq24190",
 273                .addr = 0x6b,
 274                .dev_name = "bq24190",
 275                .properties = bq24190_props,
 276                .platform_data = &bq24190_pdata,
 277        };
 278        int ret, reg, irq;
 279
 280        irq = platform_get_irq(pdev, 0);
 281        if (irq < 0) {
 282                dev_err(&pdev->dev, "Error missing irq resource\n");
 283                return -EINVAL;
 284        }
 285
 286        adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL);
 287        if (!adap)
 288                return -ENOMEM;
 289
 290        init_waitqueue_head(&adap->wait);
 291        mutex_init(&adap->adap_lock);
 292        mutex_init(&adap->irqchip_lock);
 293        adap->irqchip = cht_wc_i2c_irq_chip;
 294        adap->regmap = pmic->regmap;
 295        adap->adapter.owner = THIS_MODULE;
 296        adap->adapter.class = I2C_CLASS_HWMON;
 297        adap->adapter.algo = &cht_wc_i2c_adap_algo;
 298        strlcpy(adap->adapter.name, "PMIC I2C Adapter",
 299                sizeof(adap->adapter.name));
 300        adap->adapter.dev.parent = &pdev->dev;
 301
 302        /* Clear and activate i2c-adapter interrupts, disable client IRQ */
 303        adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK;
 304
 305        ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &reg);
 306        if (ret)
 307                return ret;
 308
 309        ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask);
 310        if (ret)
 311                return ret;
 312
 313        ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask);
 314        if (ret)
 315                return ret;
 316
 317        /* Alloc and register client IRQ */
 318        adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1,
 319                                                 &irq_domain_simple_ops, NULL);
 320        if (!adap->irq_domain)
 321                return -ENOMEM;
 322
 323        adap->client_irq = irq_create_mapping(adap->irq_domain, 0);
 324        if (!adap->client_irq) {
 325                ret = -ENOMEM;
 326                goto remove_irq_domain;
 327        }
 328
 329        irq_set_chip_data(adap->client_irq, adap);
 330        irq_set_chip_and_handler(adap->client_irq, &adap->irqchip,
 331                                 handle_simple_irq);
 332
 333        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 334                                        cht_wc_i2c_adap_thread_handler,
 335                                        IRQF_ONESHOT, "PMIC I2C Adapter", adap);
 336        if (ret)
 337                goto remove_irq_domain;
 338
 339        i2c_set_adapdata(&adap->adapter, adap);
 340        ret = i2c_add_adapter(&adap->adapter);
 341        if (ret)
 342                goto remove_irq_domain;
 343
 344        /*
 345         * Normally the Whiskey Cove PMIC is paired with a TI bq24292i charger,
 346         * connected to this i2c bus, and a max17047 fuel-gauge and a fusb302
 347         * USB Type-C controller connected to another i2c bus. In this setup
 348         * the max17047 and fusb302 devices are enumerated through an INT33FE
 349         * ACPI device. If this device is present register an i2c-client for
 350         * the TI bq24292i charger.
 351         */
 352        if (acpi_dev_present("INT33FE", NULL, -1)) {
 353                board_info.irq = adap->client_irq;
 354                adap->client = i2c_new_device(&adap->adapter, &board_info);
 355                if (!adap->client) {
 356                        ret = -ENOMEM;
 357                        goto del_adapter;
 358                }
 359        }
 360
 361        platform_set_drvdata(pdev, adap);
 362        return 0;
 363
 364del_adapter:
 365        i2c_del_adapter(&adap->adapter);
 366remove_irq_domain:
 367        irq_domain_remove(adap->irq_domain);
 368        return ret;
 369}
 370
 371static int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev)
 372{
 373        struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev);
 374
 375        if (adap->client)
 376                i2c_unregister_device(adap->client);
 377        i2c_del_adapter(&adap->adapter);
 378        irq_domain_remove(adap->irq_domain);
 379
 380        return 0;
 381}
 382
 383static const struct platform_device_id cht_wc_i2c_adap_id_table[] = {
 384        { .name = "cht_wcove_ext_chgr" },
 385        {},
 386};
 387MODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table);
 388
 389static struct platform_driver cht_wc_i2c_adap_driver = {
 390        .probe = cht_wc_i2c_adap_i2c_probe,
 391        .remove = cht_wc_i2c_adap_i2c_remove,
 392        .driver = {
 393                .name = "cht_wcove_ext_chgr",
 394        },
 395        .id_table = cht_wc_i2c_adap_id_table,
 396};
 397module_platform_driver(cht_wc_i2c_adap_driver);
 398
 399MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver");
 400MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 401MODULE_LICENSE("GPL");
 402