linux/drivers/mfd/tc3589x.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) ST-Ericsson SA 2010
   3 *
   4 * License Terms: GNU General Public License, version 2
   5 * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
   6 * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/interrupt.h>
  11#include <linux/irq.h>
  12#include <linux/irqdomain.h>
  13#include <linux/slab.h>
  14#include <linux/i2c.h>
  15#include <linux/of.h>
  16#include <linux/mfd/core.h>
  17#include <linux/mfd/tc3589x.h>
  18
  19#define TC3589x_CLKMODE_MODCTL_SLEEP            0x0
  20#define TC3589x_CLKMODE_MODCTL_OPERATION        (1 << 0)
  21
  22/**
  23 * tc3589x_reg_read() - read a single TC3589x register
  24 * @tc3589x:    Device to read from
  25 * @reg:        Register to read
  26 */
  27int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg)
  28{
  29        int ret;
  30
  31        ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg);
  32        if (ret < 0)
  33                dev_err(tc3589x->dev, "failed to read reg %#x: %d\n",
  34                        reg, ret);
  35
  36        return ret;
  37}
  38EXPORT_SYMBOL_GPL(tc3589x_reg_read);
  39
  40/**
  41 * tc3589x_reg_read() - write a single TC3589x register
  42 * @tc3589x:    Device to write to
  43 * @reg:        Register to read
  44 * @data:       Value to write
  45 */
  46int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data)
  47{
  48        int ret;
  49
  50        ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data);
  51        if (ret < 0)
  52                dev_err(tc3589x->dev, "failed to write reg %#x: %d\n",
  53                        reg, ret);
  54
  55        return ret;
  56}
  57EXPORT_SYMBOL_GPL(tc3589x_reg_write);
  58
  59/**
  60 * tc3589x_block_read() - read multiple TC3589x registers
  61 * @tc3589x:    Device to read from
  62 * @reg:        First register
  63 * @length:     Number of registers
  64 * @values:     Buffer to write to
  65 */
  66int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values)
  67{
  68        int ret;
  69
  70        ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values);
  71        if (ret < 0)
  72                dev_err(tc3589x->dev, "failed to read regs %#x: %d\n",
  73                        reg, ret);
  74
  75        return ret;
  76}
  77EXPORT_SYMBOL_GPL(tc3589x_block_read);
  78
  79/**
  80 * tc3589x_block_write() - write multiple TC3589x registers
  81 * @tc3589x:    Device to write to
  82 * @reg:        First register
  83 * @length:     Number of registers
  84 * @values:     Values to write
  85 */
  86int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length,
  87                        const u8 *values)
  88{
  89        int ret;
  90
  91        ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length,
  92                                             values);
  93        if (ret < 0)
  94                dev_err(tc3589x->dev, "failed to write regs %#x: %d\n",
  95                        reg, ret);
  96
  97        return ret;
  98}
  99EXPORT_SYMBOL_GPL(tc3589x_block_write);
 100
 101/**
 102 * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register
 103 * @tc3589x:    Device to write to
 104 * @reg:        Register to write
 105 * @mask:       Mask of bits to set
 106 * @values:     Value to set
 107 */
 108int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val)
 109{
 110        int ret;
 111
 112        mutex_lock(&tc3589x->lock);
 113
 114        ret = tc3589x_reg_read(tc3589x, reg);
 115        if (ret < 0)
 116                goto out;
 117
 118        ret &= ~mask;
 119        ret |= val;
 120
 121        ret = tc3589x_reg_write(tc3589x, reg, ret);
 122
 123out:
 124        mutex_unlock(&tc3589x->lock);
 125        return ret;
 126}
 127EXPORT_SYMBOL_GPL(tc3589x_set_bits);
 128
 129static struct resource gpio_resources[] = {
 130        {
 131                .start  = TC3589x_INT_GPIIRQ,
 132                .end    = TC3589x_INT_GPIIRQ,
 133                .flags  = IORESOURCE_IRQ,
 134        },
 135};
 136
 137static struct resource keypad_resources[] = {
 138        {
 139                .start  = TC3589x_INT_KBDIRQ,
 140                .end    = TC3589x_INT_KBDIRQ,
 141                .flags  = IORESOURCE_IRQ,
 142        },
 143};
 144
 145static struct mfd_cell tc3589x_dev_gpio[] = {
 146        {
 147                .name           = "tc3589x-gpio",
 148                .num_resources  = ARRAY_SIZE(gpio_resources),
 149                .resources      = &gpio_resources[0],
 150                .of_compatible  = "tc3589x-gpio",
 151        },
 152};
 153
 154static struct mfd_cell tc3589x_dev_keypad[] = {
 155        {
 156                .name           = "tc3589x-keypad",
 157                .num_resources  = ARRAY_SIZE(keypad_resources),
 158                .resources      = &keypad_resources[0],
 159                .of_compatible  = "tc3589x-keypad",
 160        },
 161};
 162
 163static irqreturn_t tc3589x_irq(int irq, void *data)
 164{
 165        struct tc3589x *tc3589x = data;
 166        int status;
 167
 168again:
 169        status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
 170        if (status < 0)
 171                return IRQ_NONE;
 172
 173        while (status) {
 174                int bit = __ffs(status);
 175                int virq = irq_create_mapping(tc3589x->domain, bit);
 176
 177                handle_nested_irq(virq);
 178                status &= ~(1 << bit);
 179        }
 180
 181        /*
 182         * A dummy read or write (to any register) appears to be necessary to
 183         * have the last interrupt clear (for example, GPIO IC write) take
 184         * effect. In such a case, recheck for any interrupt which is still
 185         * pending.
 186         */
 187        status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
 188        if (status)
 189                goto again;
 190
 191        return IRQ_HANDLED;
 192}
 193
 194static int tc3589x_irq_map(struct irq_domain *d, unsigned int virq,
 195                                irq_hw_number_t hwirq)
 196{
 197        struct tc3589x *tc3589x = d->host_data;
 198
 199        irq_set_chip_data(virq, tc3589x);
 200        irq_set_chip_and_handler(virq, &dummy_irq_chip,
 201                                handle_edge_irq);
 202        irq_set_nested_thread(virq, 1);
 203#ifdef CONFIG_ARM
 204        set_irq_flags(virq, IRQF_VALID);
 205#else
 206        irq_set_noprobe(virq);
 207#endif
 208
 209        return 0;
 210}
 211
 212static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
 213{
 214#ifdef CONFIG_ARM
 215        set_irq_flags(virq, 0);
 216#endif
 217        irq_set_chip_and_handler(virq, NULL, NULL);
 218        irq_set_chip_data(virq, NULL);
 219}
 220
 221static struct irq_domain_ops tc3589x_irq_ops = {
 222        .map    = tc3589x_irq_map,
 223        .unmap  = tc3589x_irq_unmap,
 224        .xlate  = irq_domain_xlate_twocell,
 225};
 226
 227static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
 228{
 229        int base = tc3589x->irq_base;
 230
 231        tc3589x->domain = irq_domain_add_simple(
 232                np, TC3589x_NR_INTERNAL_IRQS, base,
 233                &tc3589x_irq_ops, tc3589x);
 234
 235        if (!tc3589x->domain) {
 236                dev_err(tc3589x->dev, "Failed to create irqdomain\n");
 237                return -ENOSYS;
 238        }
 239
 240        return 0;
 241}
 242
 243static int tc3589x_chip_init(struct tc3589x *tc3589x)
 244{
 245        int manf, ver, ret;
 246
 247        manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE);
 248        if (manf < 0)
 249                return manf;
 250
 251        ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION);
 252        if (ver < 0)
 253                return ver;
 254
 255        if (manf != TC3589x_MANFCODE_MAGIC) {
 256                dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf);
 257                return -EINVAL;
 258        }
 259
 260        dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
 261
 262        /*
 263         * Put everything except the IRQ module into reset;
 264         * also spare the GPIO module for any pin initialization
 265         * done during pre-kernel boot
 266         */
 267        ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL,
 268                                TC3589x_RSTCTRL_TIMRST
 269                                | TC3589x_RSTCTRL_ROTRST
 270                                | TC3589x_RSTCTRL_KBDRST);
 271        if (ret < 0)
 272                return ret;
 273
 274        /* Clear the reset interrupt. */
 275        return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
 276}
 277
 278static int tc3589x_device_init(struct tc3589x *tc3589x)
 279{
 280        int ret = 0;
 281        unsigned int blocks = tc3589x->pdata->block;
 282
 283        if (blocks & TC3589x_BLOCK_GPIO) {
 284                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
 285                                      ARRAY_SIZE(tc3589x_dev_gpio), NULL,
 286                                      tc3589x->irq_base, tc3589x->domain);
 287                if (ret) {
 288                        dev_err(tc3589x->dev, "failed to add gpio child\n");
 289                        return ret;
 290                }
 291                dev_info(tc3589x->dev, "added gpio block\n");
 292        }
 293
 294        if (blocks & TC3589x_BLOCK_KEYPAD) {
 295                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
 296                                      ARRAY_SIZE(tc3589x_dev_keypad), NULL,
 297                                      tc3589x->irq_base, tc3589x->domain);
 298                if (ret) {
 299                        dev_err(tc3589x->dev, "failed to keypad child\n");
 300                        return ret;
 301                }
 302                dev_info(tc3589x->dev, "added keypad block\n");
 303        }
 304
 305        return ret;
 306}
 307
 308static int tc3589x_of_probe(struct device_node *np,
 309                        struct tc3589x_platform_data *pdata)
 310{
 311        struct device_node *child;
 312
 313        for_each_child_of_node(np, child) {
 314                if (!strcmp(child->name, "tc3589x_gpio")) {
 315                        pdata->block |= TC3589x_BLOCK_GPIO;
 316                }
 317                if (!strcmp(child->name, "tc3589x_keypad")) {
 318                        pdata->block |= TC3589x_BLOCK_KEYPAD;
 319                }
 320        }
 321
 322        return 0;
 323}
 324
 325static int tc3589x_probe(struct i2c_client *i2c,
 326                                   const struct i2c_device_id *id)
 327{
 328        struct tc3589x_platform_data *pdata = i2c->dev.platform_data;
 329        struct device_node *np = i2c->dev.of_node;
 330        struct tc3589x *tc3589x;
 331        int ret;
 332
 333        if (!pdata) {
 334                if (np) {
 335                        pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
 336                        if (!pdata)
 337                                return -ENOMEM;
 338
 339                        ret = tc3589x_of_probe(np, pdata);
 340                        if (ret)
 341                                return ret;
 342                }
 343                else {
 344                        dev_err(&i2c->dev, "No platform data or DT found\n");
 345                        return -EINVAL;
 346                }
 347        }
 348
 349        if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
 350                                     | I2C_FUNC_SMBUS_I2C_BLOCK))
 351                return -EIO;
 352
 353        tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL);
 354        if (!tc3589x)
 355                return -ENOMEM;
 356
 357        mutex_init(&tc3589x->lock);
 358
 359        tc3589x->dev = &i2c->dev;
 360        tc3589x->i2c = i2c;
 361        tc3589x->pdata = pdata;
 362        tc3589x->irq_base = pdata->irq_base;
 363        tc3589x->num_gpio = id->driver_data;
 364
 365        i2c_set_clientdata(i2c, tc3589x);
 366
 367        ret = tc3589x_chip_init(tc3589x);
 368        if (ret)
 369                goto out_free;
 370
 371        ret = tc3589x_irq_init(tc3589x, np);
 372        if (ret)
 373                goto out_free;
 374
 375        ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
 376                                   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 377                                   "tc3589x", tc3589x);
 378        if (ret) {
 379                dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
 380                goto out_free;
 381        }
 382
 383        ret = tc3589x_device_init(tc3589x);
 384        if (ret) {
 385                dev_err(tc3589x->dev, "failed to add child devices\n");
 386                goto out_freeirq;
 387        }
 388
 389        return 0;
 390
 391out_freeirq:
 392        free_irq(tc3589x->i2c->irq, tc3589x);
 393out_free:
 394        kfree(tc3589x);
 395        return ret;
 396}
 397
 398static int tc3589x_remove(struct i2c_client *client)
 399{
 400        struct tc3589x *tc3589x = i2c_get_clientdata(client);
 401
 402        mfd_remove_devices(tc3589x->dev);
 403
 404        free_irq(tc3589x->i2c->irq, tc3589x);
 405
 406        kfree(tc3589x);
 407
 408        return 0;
 409}
 410
 411#ifdef CONFIG_PM_SLEEP
 412static int tc3589x_suspend(struct device *dev)
 413{
 414        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 415        struct i2c_client *client = tc3589x->i2c;
 416        int ret = 0;
 417
 418        /* put the system to sleep mode */
 419        if (!device_may_wakeup(&client->dev))
 420                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 421                                TC3589x_CLKMODE_MODCTL_SLEEP);
 422
 423        return ret;
 424}
 425
 426static int tc3589x_resume(struct device *dev)
 427{
 428        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 429        struct i2c_client *client = tc3589x->i2c;
 430        int ret = 0;
 431
 432        /* enable the system into operation */
 433        if (!device_may_wakeup(&client->dev))
 434                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 435                                TC3589x_CLKMODE_MODCTL_OPERATION);
 436
 437        return ret;
 438}
 439#endif
 440
 441static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
 442
 443static const struct i2c_device_id tc3589x_id[] = {
 444        { "tc3589x", 24 },
 445        { }
 446};
 447MODULE_DEVICE_TABLE(i2c, tc3589x_id);
 448
 449static struct i2c_driver tc3589x_driver = {
 450        .driver.name    = "tc3589x",
 451        .driver.owner   = THIS_MODULE,
 452        .driver.pm      = &tc3589x_dev_pm_ops,
 453        .probe          = tc3589x_probe,
 454        .remove         = tc3589x_remove,
 455        .id_table       = tc3589x_id,
 456};
 457
 458static int __init tc3589x_init(void)
 459{
 460        return i2c_add_driver(&tc3589x_driver);
 461}
 462subsys_initcall(tc3589x_init);
 463
 464static void __exit tc3589x_exit(void)
 465{
 466        i2c_del_driver(&tc3589x_driver);
 467}
 468module_exit(tc3589x_exit);
 469
 470MODULE_LICENSE("GPL v2");
 471MODULE_DESCRIPTION("TC3589x MFD core driver");
 472MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
 473
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.