Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Retu/Tahvo MFD driver
0003  *
0004  * Copyright (C) 2004, 2005 Nokia Corporation
0005  *
0006  * Based on code written by Juha Yrjölä, David Weinehall and Mikko Ylinen.
0007  * Rewritten by Aaro Koskinen.
0008  *
0009  * This file is subject to the terms and conditions of the GNU General
0010  * Public License. See the file "COPYING" in the main directory of this
0011  * archive for more details.
0012  *
0013  * This program is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016  * GNU General Public License for more details.
0017  */
0018 
0019 #include <linux/err.h>
0020 #include <linux/i2c.h>
0021 #include <linux/irq.h>
0022 #include <linux/slab.h>
0023 #include <linux/mutex.h>
0024 #include <linux/module.h>
0025 #include <linux/regmap.h>
0026 #include <linux/mfd/core.h>
0027 #include <linux/mfd/retu.h>
0028 #include <linux/interrupt.h>
0029 #include <linux/moduleparam.h>
0030 
0031 /* Registers */
0032 #define RETU_REG_ASICR      0x00        /* ASIC ID and revision */
0033 #define RETU_REG_ASICR_VILMA    (1 << 7)    /* Bit indicating Vilma */
0034 #define RETU_REG_IDR        0x01        /* Interrupt ID */
0035 #define RETU_REG_IMR        0x02        /* Interrupt mask (Retu) */
0036 #define TAHVO_REG_IMR       0x03        /* Interrupt mask (Tahvo) */
0037 
0038 /* Interrupt sources */
0039 #define RETU_INT_PWR        0       /* Power button */
0040 
0041 struct retu_dev {
0042     struct regmap           *regmap;
0043     struct device           *dev;
0044     struct mutex            mutex;
0045     struct regmap_irq_chip_data *irq_data;
0046 };
0047 
0048 static const struct resource retu_pwrbutton_res[] = {
0049     {
0050         .name   = "retu-pwrbutton",
0051         .start  = RETU_INT_PWR,
0052         .end    = RETU_INT_PWR,
0053         .flags  = IORESOURCE_IRQ,
0054     },
0055 };
0056 
0057 static const struct mfd_cell retu_devs[] = {
0058     {
0059         .name       = "retu-wdt"
0060     },
0061     {
0062         .name       = "retu-pwrbutton",
0063         .resources  = retu_pwrbutton_res,
0064         .num_resources  = ARRAY_SIZE(retu_pwrbutton_res),
0065     }
0066 };
0067 
0068 static struct regmap_irq retu_irqs[] = {
0069     [RETU_INT_PWR] = {
0070         .mask = 1 << RETU_INT_PWR,
0071     }
0072 };
0073 
0074 static struct regmap_irq_chip retu_irq_chip = {
0075     .name       = "RETU",
0076     .irqs       = retu_irqs,
0077     .num_irqs   = ARRAY_SIZE(retu_irqs),
0078     .num_regs   = 1,
0079     .status_base    = RETU_REG_IDR,
0080     .mask_base  = RETU_REG_IMR,
0081     .ack_base   = RETU_REG_IDR,
0082 };
0083 
0084 /* Retu device registered for the power off. */
0085 static struct retu_dev *retu_pm_power_off;
0086 
0087 static const struct resource tahvo_usb_res[] = {
0088     {
0089         .name   = "tahvo-usb",
0090         .start  = TAHVO_INT_VBUS,
0091         .end    = TAHVO_INT_VBUS,
0092         .flags  = IORESOURCE_IRQ,
0093     },
0094 };
0095 
0096 static const struct mfd_cell tahvo_devs[] = {
0097     {
0098         .name       = "tahvo-usb",
0099         .resources  = tahvo_usb_res,
0100         .num_resources  = ARRAY_SIZE(tahvo_usb_res),
0101     },
0102 };
0103 
0104 static struct regmap_irq tahvo_irqs[] = {
0105     [TAHVO_INT_VBUS] = {
0106         .mask = 1 << TAHVO_INT_VBUS,
0107     }
0108 };
0109 
0110 static struct regmap_irq_chip tahvo_irq_chip = {
0111     .name       = "TAHVO",
0112     .irqs       = tahvo_irqs,
0113     .num_irqs   = ARRAY_SIZE(tahvo_irqs),
0114     .num_regs   = 1,
0115     .status_base    = RETU_REG_IDR,
0116     .mask_base  = TAHVO_REG_IMR,
0117     .ack_base   = RETU_REG_IDR,
0118 };
0119 
0120 static const struct retu_data {
0121     char            *chip_name;
0122     char            *companion_name;
0123     struct regmap_irq_chip  *irq_chip;
0124     const struct mfd_cell   *children;
0125     int         nchildren;
0126 } retu_data[] = {
0127     [0] = {
0128         .chip_name  = "Retu",
0129         .companion_name = "Vilma",
0130         .irq_chip   = &retu_irq_chip,
0131         .children   = retu_devs,
0132         .nchildren  = ARRAY_SIZE(retu_devs),
0133     },
0134     [1] = {
0135         .chip_name  = "Tahvo",
0136         .companion_name = "Betty",
0137         .irq_chip   = &tahvo_irq_chip,
0138         .children   = tahvo_devs,
0139         .nchildren  = ARRAY_SIZE(tahvo_devs),
0140     }
0141 };
0142 
0143 int retu_read(struct retu_dev *rdev, u8 reg)
0144 {
0145     int ret;
0146     int value;
0147 
0148     mutex_lock(&rdev->mutex);
0149     ret = regmap_read(rdev->regmap, reg, &value);
0150     mutex_unlock(&rdev->mutex);
0151 
0152     return ret ? ret : value;
0153 }
0154 EXPORT_SYMBOL_GPL(retu_read);
0155 
0156 int retu_write(struct retu_dev *rdev, u8 reg, u16 data)
0157 {
0158     int ret;
0159 
0160     mutex_lock(&rdev->mutex);
0161     ret = regmap_write(rdev->regmap, reg, data);
0162     mutex_unlock(&rdev->mutex);
0163 
0164     return ret;
0165 }
0166 EXPORT_SYMBOL_GPL(retu_write);
0167 
0168 static void retu_power_off(void)
0169 {
0170     struct retu_dev *rdev = retu_pm_power_off;
0171     int reg;
0172 
0173     mutex_lock(&retu_pm_power_off->mutex);
0174 
0175     /* Ignore power button state */
0176     regmap_read(rdev->regmap, RETU_REG_CC1, &reg);
0177     regmap_write(rdev->regmap, RETU_REG_CC1, reg | 2);
0178 
0179     /* Expire watchdog immediately */
0180     regmap_write(rdev->regmap, RETU_REG_WATCHDOG, 0);
0181 
0182     /* Wait for poweroff */
0183     for (;;)
0184         cpu_relax();
0185 
0186     mutex_unlock(&retu_pm_power_off->mutex);
0187 }
0188 
0189 static int retu_regmap_read(void *context, const void *reg, size_t reg_size,
0190                 void *val, size_t val_size)
0191 {
0192     int ret;
0193     struct device *dev = context;
0194     struct i2c_client *i2c = to_i2c_client(dev);
0195 
0196     BUG_ON(reg_size != 1 || val_size != 2);
0197 
0198     ret = i2c_smbus_read_word_data(i2c, *(u8 const *)reg);
0199     if (ret < 0)
0200         return ret;
0201 
0202     *(u16 *)val = ret;
0203     return 0;
0204 }
0205 
0206 static int retu_regmap_write(void *context, const void *data, size_t count)
0207 {
0208     u8 reg;
0209     u16 val;
0210     struct device *dev = context;
0211     struct i2c_client *i2c = to_i2c_client(dev);
0212 
0213     BUG_ON(count != sizeof(reg) + sizeof(val));
0214     memcpy(&reg, data, sizeof(reg));
0215     memcpy(&val, data + sizeof(reg), sizeof(val));
0216     return i2c_smbus_write_word_data(i2c, reg, val);
0217 }
0218 
0219 static struct regmap_bus retu_bus = {
0220     .read = retu_regmap_read,
0221     .write = retu_regmap_write,
0222     .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
0223 };
0224 
0225 static const struct regmap_config retu_config = {
0226     .reg_bits = 8,
0227     .val_bits = 16,
0228 };
0229 
0230 static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
0231 {
0232     struct retu_data const *rdat;
0233     struct retu_dev *rdev;
0234     int ret;
0235 
0236     if (i2c->addr > ARRAY_SIZE(retu_data))
0237         return -ENODEV;
0238     rdat = &retu_data[i2c->addr - 1];
0239 
0240     rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL);
0241     if (rdev == NULL)
0242         return -ENOMEM;
0243 
0244     i2c_set_clientdata(i2c, rdev);
0245     rdev->dev = &i2c->dev;
0246     mutex_init(&rdev->mutex);
0247     rdev->regmap = devm_regmap_init(&i2c->dev, &retu_bus, &i2c->dev,
0248                     &retu_config);
0249     if (IS_ERR(rdev->regmap))
0250         return PTR_ERR(rdev->regmap);
0251 
0252     ret = retu_read(rdev, RETU_REG_ASICR);
0253     if (ret < 0) {
0254         dev_err(rdev->dev, "could not read %s revision: %d\n",
0255             rdat->chip_name, ret);
0256         return ret;
0257     }
0258 
0259     dev_info(rdev->dev, "%s%s%s v%d.%d found\n", rdat->chip_name,
0260          (ret & RETU_REG_ASICR_VILMA) ? " & " : "",
0261          (ret & RETU_REG_ASICR_VILMA) ? rdat->companion_name : "",
0262          (ret >> 4) & 0x7, ret & 0xf);
0263 
0264     /* Mask all interrupts. */
0265     ret = retu_write(rdev, rdat->irq_chip->mask_base, 0xffff);
0266     if (ret < 0)
0267         return ret;
0268 
0269     ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1,
0270                   rdat->irq_chip, &rdev->irq_data);
0271     if (ret < 0)
0272         return ret;
0273 
0274     ret = mfd_add_devices(rdev->dev, -1, rdat->children, rdat->nchildren,
0275                   NULL, regmap_irq_chip_get_base(rdev->irq_data),
0276                   NULL);
0277     if (ret < 0) {
0278         regmap_del_irq_chip(i2c->irq, rdev->irq_data);
0279         return ret;
0280     }
0281 
0282     if (i2c->addr == 1 && !pm_power_off) {
0283         retu_pm_power_off = rdev;
0284         pm_power_off      = retu_power_off;
0285     }
0286 
0287     return 0;
0288 }
0289 
0290 static int retu_remove(struct i2c_client *i2c)
0291 {
0292     struct retu_dev *rdev = i2c_get_clientdata(i2c);
0293 
0294     if (retu_pm_power_off == rdev) {
0295         pm_power_off      = NULL;
0296         retu_pm_power_off = NULL;
0297     }
0298     mfd_remove_devices(rdev->dev);
0299     regmap_del_irq_chip(i2c->irq, rdev->irq_data);
0300 
0301     return 0;
0302 }
0303 
0304 static const struct i2c_device_id retu_id[] = {
0305     { "retu", 0 },
0306     { "tahvo", 0 },
0307     { }
0308 };
0309 MODULE_DEVICE_TABLE(i2c, retu_id);
0310 
0311 static const struct of_device_id retu_of_match[] = {
0312     { .compatible = "nokia,retu" },
0313     { .compatible = "nokia,tahvo" },
0314     { }
0315 };
0316 MODULE_DEVICE_TABLE(of, retu_of_match);
0317 
0318 static struct i2c_driver retu_driver = {
0319     .driver     = {
0320         .name = "retu-mfd",
0321         .of_match_table = retu_of_match,
0322     },
0323     .probe      = retu_probe,
0324     .remove     = retu_remove,
0325     .id_table   = retu_id,
0326 };
0327 module_i2c_driver(retu_driver);
0328 
0329 MODULE_DESCRIPTION("Retu MFD driver");
0330 MODULE_AUTHOR("Juha Yrjölä");
0331 MODULE_AUTHOR("David Weinehall");
0332 MODULE_AUTHOR("Mikko Ylinen");
0333 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
0334 MODULE_LICENSE("GPL");