linux/drivers/mfd/htc-pasic3.c
<<
>>
Prefs
   1/*
   2 * Core driver for HTC PASIC3 LED/DS1WM chip.
   3 *
   4 * Copyright (C) 2006 Philipp Zabel <philipp.zabel@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; version 2 of the License.
   9 */
  10
  11#include <linux/init.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14
  15#include <linux/ds1wm.h>
  16#include <linux/gpio.h>
  17#include <linux/io.h>
  18#include <linux/irq.h>
  19#include <linux/interrupt.h>
  20#include <linux/mfd/htc-pasic3.h>
  21
  22struct pasic3_data {
  23        void __iomem *mapping;
  24        unsigned int bus_shift;
  25        struct platform_device *ds1wm_pdev;
  26        struct platform_device *led_pdev;
  27};
  28
  29#define REG_ADDR  5
  30#define REG_DATA  6
  31
  32#define READ_MODE 0x80
  33
  34/*
  35 * write to a secondary register on the PASIC3
  36 */
  37void pasic3_write_register(struct device *dev, u32 reg, u8 val)
  38{
  39        struct pasic3_data *asic = dev->driver_data;
  40        int bus_shift = asic->bus_shift;
  41        void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
  42        void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
  43
  44        __raw_writeb(~READ_MODE & reg, addr);
  45        __raw_writeb(val, data);
  46}
  47EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */
  48
  49/*
  50 * read from a secondary register on the PASIC3
  51 */
  52u8 pasic3_read_register(struct device *dev, u32 reg)
  53{
  54        struct pasic3_data *asic = dev->driver_data;
  55        int bus_shift = asic->bus_shift;
  56        void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
  57        void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
  58
  59        __raw_writeb(READ_MODE | reg, addr);
  60        return __raw_readb(data);
  61}
  62EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */
  63
  64/*
  65 * LEDs
  66 */
  67
  68static int led_device_add(struct device *pasic3_dev,
  69                                const struct pasic3_leds_machinfo *pdata)
  70{
  71        struct pasic3_data *asic = pasic3_dev->driver_data;
  72        struct platform_device *pdev;
  73        int ret;
  74
  75        pdev = platform_device_alloc("pasic3-led", -1);
  76        if (!pdev) {
  77                dev_dbg(pasic3_dev, "failed to allocate LED platform device\n");
  78                return -ENOMEM;
  79        }
  80
  81        ret = platform_device_add_data(pdev, pdata,
  82                                        sizeof(struct pasic3_leds_machinfo));
  83        if (ret < 0) {
  84                dev_dbg(pasic3_dev, "failed to add LED platform data\n");
  85                goto exit_pdev_put;
  86        }
  87
  88        pdev->dev.parent = pasic3_dev;
  89        ret = platform_device_add(pdev);
  90        if (ret < 0) {
  91                dev_dbg(pasic3_dev, "failed to add LED platform device\n");
  92                goto exit_pdev_put;
  93        }
  94
  95        asic->led_pdev = pdev;
  96        return 0;
  97
  98exit_pdev_put:
  99        platform_device_put(pdev);
 100        return ret;
 101}
 102
 103/*
 104 * DS1WM
 105 */
 106
 107static void ds1wm_enable(struct platform_device *pdev)
 108{
 109        struct device *dev = pdev->dev.parent;
 110        int c;
 111
 112        c = pasic3_read_register(dev, 0x28);
 113        pasic3_write_register(dev, 0x28, c & 0x7f);
 114
 115        dev_dbg(dev, "DS1WM OWM_EN low (active) %02x\n", c & 0x7f);
 116}
 117
 118static void ds1wm_disable(struct platform_device *pdev)
 119{
 120        struct device *dev = pdev->dev.parent;
 121        int c;
 122
 123        c = pasic3_read_register(dev, 0x28);
 124        pasic3_write_register(dev, 0x28, c | 0x80);
 125
 126        dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x\n", c | 0x80);
 127}
 128
 129static struct ds1wm_platform_data ds1wm_pdata = {
 130        .bus_shift = 2,
 131        .enable    = ds1wm_enable,
 132        .disable   = ds1wm_disable,
 133};
 134
 135static int ds1wm_device_add(struct platform_device *pasic3_pdev, int bus_shift)
 136{
 137        struct device *pasic3_dev = &pasic3_pdev->dev;
 138        struct pasic3_data *asic = pasic3_dev->driver_data;
 139        struct platform_device *pdev;
 140        int ret;
 141
 142        pdev = platform_device_alloc("ds1wm", -1);
 143        if (!pdev) {
 144                dev_dbg(pasic3_dev, "failed to allocate DS1WM platform device\n");
 145                return -ENOMEM;
 146        }
 147
 148        ret = platform_device_add_resources(pdev, pasic3_pdev->resource,
 149                                                pasic3_pdev->num_resources);
 150        if (ret < 0) {
 151                dev_dbg(pasic3_dev, "failed to add DS1WM resources\n");
 152                goto exit_pdev_put;
 153        }
 154
 155        ds1wm_pdata.bus_shift = asic->bus_shift;
 156        ret = platform_device_add_data(pdev, &ds1wm_pdata,
 157                                        sizeof(struct ds1wm_platform_data));
 158        if (ret < 0) {
 159                dev_dbg(pasic3_dev, "failed to add DS1WM platform data\n");
 160                goto exit_pdev_put;
 161        }
 162
 163        pdev->dev.parent = pasic3_dev;
 164        ret = platform_device_add(pdev);
 165        if (ret < 0) {
 166                dev_dbg(pasic3_dev, "failed to add DS1WM platform device\n");
 167                goto exit_pdev_put;
 168        }
 169
 170        asic->ds1wm_pdev = pdev;
 171        return 0;
 172
 173exit_pdev_put:
 174        platform_device_put(pdev);
 175        return ret;
 176}
 177
 178static int __init pasic3_probe(struct platform_device *pdev)
 179{
 180        struct pasic3_platform_data *pdata = pdev->dev.platform_data;
 181        struct device *dev = &pdev->dev;
 182        struct pasic3_data *asic;
 183        struct resource *r;
 184        int ret;
 185
 186        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 187        if (!r)
 188                return -ENXIO;
 189
 190        if (!request_mem_region(r->start, r->end - r->start + 1, "pasic3"))
 191                return -EBUSY;
 192
 193        asic = kzalloc(sizeof(struct pasic3_data), GFP_KERNEL);
 194        if (!asic)
 195                return -ENOMEM;
 196
 197        platform_set_drvdata(pdev, asic);
 198
 199        if (pdata && pdata->bus_shift)
 200                asic->bus_shift = pdata->bus_shift;
 201        else
 202                asic->bus_shift = 2;
 203
 204        asic->mapping = ioremap(r->start, r->end - r->start + 1);
 205        if (!asic->mapping) {
 206                dev_err(dev, "couldn't ioremap PASIC3\n");
 207                kfree(asic);
 208                return -ENOMEM;
 209        }
 210
 211        ret = ds1wm_device_add(pdev, asic->bus_shift);
 212        if (ret < 0)
 213                dev_warn(dev, "failed to register DS1WM\n");
 214
 215        if (pdata->led_pdata) {
 216                ret = led_device_add(dev, pdata->led_pdata);
 217                if (ret < 0)
 218                        dev_warn(dev, "failed to register LED device\n");
 219        }
 220
 221        return 0;
 222}
 223
 224static int pasic3_remove(struct platform_device *pdev)
 225{
 226        struct pasic3_data *asic = platform_get_drvdata(pdev);
 227        struct resource *r;
 228
 229        if (asic->led_pdev)
 230                platform_device_unregister(asic->led_pdev);
 231        if (asic->ds1wm_pdev)
 232                platform_device_unregister(asic->ds1wm_pdev);
 233
 234        iounmap(asic->mapping);
 235        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 236        release_mem_region(r->start, r->end - r->start + 1);
 237        kfree(asic);
 238        return 0;
 239}
 240
 241MODULE_ALIAS("platform:pasic3");
 242
 243static struct platform_driver pasic3_driver = {
 244        .driver         = {
 245                .name   = "pasic3",
 246        },
 247        .remove         = pasic3_remove,
 248};
 249
 250static int __init pasic3_base_init(void)
 251{
 252        return platform_driver_probe(&pasic3_driver, pasic3_probe);
 253}
 254
 255static void __exit pasic3_base_exit(void)
 256{
 257        platform_driver_unregister(&pasic3_driver);
 258}
 259
 260module_init(pasic3_base_init);
 261module_exit(pasic3_base_exit);
 262
 263MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
 264MODULE_DESCRIPTION("Core driver for HTC PASIC3");
 265MODULE_LICENSE("GPL");
 266