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        if (base) {
 232                tc3589x->domain = irq_domain_add_legacy(
 233                        NULL, TC3589x_NR_INTERNAL_IRQS, base,
 234                        0, &tc3589x_irq_ops, tc3589x);
 235        }
 236        else {
 237                tc3589x->domain = irq_domain_add_linear(
 238                        np, TC3589x_NR_INTERNAL_IRQS,
 239                        &tc3589x_irq_ops, tc3589x);
 240        }
 241
 242        if (!tc3589x->domain) {
 243                dev_err(tc3589x->dev, "Failed to create irqdomain\n");
 244                return -ENOSYS;
 245        }
 246
 247        return 0;
 248}
 249
 250static int tc3589x_chip_init(struct tc3589x *tc3589x)
 251{
 252        int manf, ver, ret;
 253
 254        manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE);
 255        if (manf < 0)
 256                return manf;
 257
 258        ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION);
 259        if (ver < 0)
 260                return ver;
 261
 262        if (manf != TC3589x_MANFCODE_MAGIC) {
 263                dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf);
 264                return -EINVAL;
 265        }
 266
 267        dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
 268
 269        /*
 270         * Put everything except the IRQ module into reset;
 271         * also spare the GPIO module for any pin initialization
 272         * done during pre-kernel boot
 273         */
 274        ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL,
 275                                TC3589x_RSTCTRL_TIMRST
 276                                | TC3589x_RSTCTRL_ROTRST
 277                                | TC3589x_RSTCTRL_KBDRST);
 278        if (ret < 0)
 279                return ret;
 280
 281        /* Clear the reset interrupt. */
 282        return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
 283}
 284
 285static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
 286{
 287        int ret = 0;
 288        unsigned int blocks = tc3589x->pdata->block;
 289
 290        if (blocks & TC3589x_BLOCK_GPIO) {
 291                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
 292                                      ARRAY_SIZE(tc3589x_dev_gpio), NULL,
 293                                      tc3589x->irq_base, tc3589x->domain);
 294                if (ret) {
 295                        dev_err(tc3589x->dev, "failed to add gpio child\n");
 296                        return ret;
 297                }
 298                dev_info(tc3589x->dev, "added gpio block\n");
 299        }
 300
 301        if (blocks & TC3589x_BLOCK_KEYPAD) {
 302                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
 303                                      ARRAY_SIZE(tc3589x_dev_keypad), NULL,
 304                                      tc3589x->irq_base, tc3589x->domain);
 305                if (ret) {
 306                        dev_err(tc3589x->dev, "failed to keypad child\n");
 307                        return ret;
 308                }
 309                dev_info(tc3589x->dev, "added keypad block\n");
 310        }
 311
 312        return ret;
 313}
 314
 315static int tc3589x_of_probe(struct device_node *np,
 316                        struct tc3589x_platform_data *pdata)
 317{
 318        struct device_node *child;
 319
 320        for_each_child_of_node(np, child) {
 321                if (!strcmp(child->name, "tc3589x_gpio")) {
 322                        pdata->block |= TC3589x_BLOCK_GPIO;
 323                }
 324                if (!strcmp(child->name, "tc3589x_keypad")) {
 325                        pdata->block |= TC3589x_BLOCK_KEYPAD;
 326                }
 327        }
 328
 329        return 0;
 330}
 331
 332static int __devinit tc3589x_probe(struct i2c_client *i2c,
 333                                   const struct i2c_device_id *id)
 334{
 335        struct tc3589x_platform_data *pdata = i2c->dev.platform_data;
 336        struct device_node *np = i2c->dev.of_node;
 337        struct tc3589x *tc3589x;
 338        int ret;
 339
 340        if (!pdata) {
 341                if (np) {
 342                        pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
 343                        if (!pdata)
 344                                return -ENOMEM;
 345
 346                        ret = tc3589x_of_probe(np, pdata);
 347                        if (ret)
 348                                return ret;
 349                }
 350                else {
 351                        dev_err(&i2c->dev, "No platform data or DT found\n");
 352                        return -EINVAL;
 353                }
 354        }
 355
 356        if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
 357                                     | I2C_FUNC_SMBUS_I2C_BLOCK))
 358                return -EIO;
 359
 360        tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL);
 361        if (!tc3589x)
 362                return -ENOMEM;
 363
 364        mutex_init(&tc3589x->lock);
 365
 366        tc3589x->dev = &i2c->dev;
 367        tc3589x->i2c = i2c;
 368        tc3589x->pdata = pdata;
 369        tc3589x->irq_base = pdata->irq_base;
 370        tc3589x->num_gpio = id->driver_data;
 371
 372        i2c_set_clientdata(i2c, tc3589x);
 373
 374        ret = tc3589x_chip_init(tc3589x);
 375        if (ret)
 376                goto out_free;
 377
 378        ret = tc3589x_irq_init(tc3589x, np);
 379        if (ret)
 380                goto out_free;
 381
 382        ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
 383                                   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 384                                   "tc3589x", tc3589x);
 385        if (ret) {
 386                dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
 387                goto out_free;
 388        }
 389
 390        ret = tc3589x_device_init(tc3589x);
 391        if (ret) {
 392                dev_err(tc3589x->dev, "failed to add child devices\n");
 393                goto out_freeirq;
 394        }
 395
 396        return 0;
 397
 398out_freeirq:
 399        free_irq(tc3589x->i2c->irq, tc3589x);
 400out_free:
 401        kfree(tc3589x);
 402        return ret;
 403}
 404
 405static int __devexit tc3589x_remove(struct i2c_client *client)
 406{
 407        struct tc3589x *tc3589x = i2c_get_clientdata(client);
 408
 409        mfd_remove_devices(tc3589x->dev);
 410
 411        free_irq(tc3589x->i2c->irq, tc3589x);
 412
 413        kfree(tc3589x);
 414
 415        return 0;
 416}
 417
 418#ifdef CONFIG_PM_SLEEP
 419static int tc3589x_suspend(struct device *dev)
 420{
 421        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 422        struct i2c_client *client = tc3589x->i2c;
 423        int ret = 0;
 424
 425        /* put the system to sleep mode */
 426        if (!device_may_wakeup(&client->dev))
 427                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 428                                TC3589x_CLKMODE_MODCTL_SLEEP);
 429
 430        return ret;
 431}
 432
 433static int tc3589x_resume(struct device *dev)
 434{
 435        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 436        struct i2c_client *client = tc3589x->i2c;
 437        int ret = 0;
 438
 439        /* enable the system into operation */
 440        if (!device_may_wakeup(&client->dev))
 441                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 442                                TC3589x_CLKMODE_MODCTL_OPERATION);
 443
 444        return ret;
 445}
 446#endif
 447
 448static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
 449
 450static const struct i2c_device_id tc3589x_id[] = {
 451        { "tc3589x", 24 },
 452        { }
 453};
 454MODULE_DEVICE_TABLE(i2c, tc3589x_id);
 455
 456static struct i2c_driver tc3589x_driver = {
 457        .driver.name    = "tc3589x",
 458        .driver.owner   = THIS_MODULE,
 459        .driver.pm      = &tc3589x_dev_pm_ops,
 460        .probe          = tc3589x_probe,
 461        .remove         = __devexit_p(tc3589x_remove),
 462        .id_table       = tc3589x_id,
 463};
 464
 465static int __init tc3589x_init(void)
 466{
 467        return i2c_add_driver(&tc3589x_driver);
 468}
 469subsys_initcall(tc3589x_init);
 470
 471static void __exit tc3589x_exit(void)
 472{
 473        i2c_del_driver(&tc3589x_driver);
 474}
 475module_exit(tc3589x_exit);
 476
 477MODULE_LICENSE("GPL v2");
 478MODULE_DESCRIPTION("TC3589x MFD core driver");
 479MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
 480
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.