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 = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x),
 354                                GFP_KERNEL);
 355        if (!tc3589x)
 356                return -ENOMEM;
 357
 358        mutex_init(&tc3589x->lock);
 359
 360        tc3589x->dev = &i2c->dev;
 361        tc3589x->i2c = i2c;
 362        tc3589x->pdata = pdata;
 363        tc3589x->irq_base = pdata->irq_base;
 364        tc3589x->num_gpio = id->driver_data;
 365
 366        i2c_set_clientdata(i2c, tc3589x);
 367
 368        ret = tc3589x_chip_init(tc3589x);
 369        if (ret)
 370                return ret;
 371
 372        ret = tc3589x_irq_init(tc3589x, np);
 373        if (ret)
 374                return ret;
 375
 376        ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
 377                                   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 378                                   "tc3589x", tc3589x);
 379        if (ret) {
 380                dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
 381                return ret;
 382        }
 383
 384        ret = tc3589x_device_init(tc3589x);
 385        if (ret) {
 386                dev_err(tc3589x->dev, "failed to add child devices\n");
 387                return ret;
 388        }
 389
 390        return 0;
 391}
 392
 393static int tc3589x_remove(struct i2c_client *client)
 394{
 395        struct tc3589x *tc3589x = i2c_get_clientdata(client);
 396
 397        mfd_remove_devices(tc3589x->dev);
 398
 399        return 0;
 400}
 401
 402#ifdef CONFIG_PM_SLEEP
 403static int tc3589x_suspend(struct device *dev)
 404{
 405        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 406        struct i2c_client *client = tc3589x->i2c;
 407        int ret = 0;
 408
 409        /* put the system to sleep mode */
 410        if (!device_may_wakeup(&client->dev))
 411                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 412                                TC3589x_CLKMODE_MODCTL_SLEEP);
 413
 414        return ret;
 415}
 416
 417static int tc3589x_resume(struct device *dev)
 418{
 419        struct tc3589x *tc3589x = dev_get_drvdata(dev);
 420        struct i2c_client *client = tc3589x->i2c;
 421        int ret = 0;
 422
 423        /* enable the system into operation */
 424        if (!device_may_wakeup(&client->dev))
 425                ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
 426                                TC3589x_CLKMODE_MODCTL_OPERATION);
 427
 428        return ret;
 429}
 430#endif
 431
 432static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
 433
 434static const struct i2c_device_id tc3589x_id[] = {
 435        { "tc3589x", 24 },
 436        { }
 437};
 438MODULE_DEVICE_TABLE(i2c, tc3589x_id);
 439
 440static struct i2c_driver tc3589x_driver = {
 441        .driver.name    = "tc3589x",
 442        .driver.owner   = THIS_MODULE,
 443        .driver.pm      = &tc3589x_dev_pm_ops,
 444        .probe          = tc3589x_probe,
 445        .remove         = tc3589x_remove,
 446        .id_table       = tc3589x_id,
 447};
 448
 449static int __init tc3589x_init(void)
 450{
 451        return i2c_add_driver(&tc3589x_driver);
 452}
 453subsys_initcall(tc3589x_init);
 454
 455static void __exit tc3589x_exit(void)
 456{
 457        i2c_del_driver(&tc3589x_driver);
 458}
 459module_exit(tc3589x_exit);
 460
 461MODULE_LICENSE("GPL v2");
 462MODULE_DESCRIPTION("TC3589x MFD core driver");
 463MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
 464
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.