linux/drivers/mfd/intel_msic.c
<<
>>
Prefs
   1/*
   2 * Driver for Intel MSIC
   3 *
   4 * Copyright (C) 2011, Intel Corporation
   5 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/gpio.h>
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/mfd/core.h>
  16#include <linux/mfd/intel_msic.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19
  20#include <asm/intel_scu_ipc.h>
  21
  22#define MSIC_VENDOR(id)         ((id >> 6) & 3)
  23#define MSIC_VERSION(id)        (id & 0x3f)
  24#define MSIC_MAJOR(id)          ('A' + ((id >> 3) & 7))
  25#define MSIC_MINOR(id)          (id & 7)
  26
  27/*
  28 * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE.
  29 * Since IRQ block starts from address 0x002 we need to substract that from
  30 * the actual IRQ status register address.
  31 */
  32#define MSIC_IRQ_STATUS(x)      (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2))
  33#define MSIC_IRQ_STATUS_ACCDET  MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET)
  34
  35/*
  36 * The SCU hardware has limitation of 16 bytes per read/write buffer on
  37 * Medfield.
  38 */
  39#define SCU_IPC_RWBUF_LIMIT     16
  40
  41/**
  42 * struct intel_msic - an MSIC MFD instance
  43 * @pdev: pointer to the platform device
  44 * @vendor: vendor ID
  45 * @version: chip version
  46 * @irq_base: base address of the mapped MSIC SRAM interrupt tree
  47 */
  48struct intel_msic {
  49        struct platform_device          *pdev;
  50        unsigned                        vendor;
  51        unsigned                        version;
  52        void __iomem                    *irq_base;
  53};
  54
  55static struct resource msic_touch_resources[] = {
  56        {
  57                .flags          = IORESOURCE_IRQ,
  58        },
  59};
  60
  61static struct resource msic_adc_resources[] = {
  62        {
  63                .flags          = IORESOURCE_IRQ,
  64        },
  65};
  66
  67static struct resource msic_battery_resources[] = {
  68        {
  69                .flags          = IORESOURCE_IRQ,
  70        },
  71};
  72
  73static struct resource msic_gpio_resources[] = {
  74        {
  75                .flags          = IORESOURCE_IRQ,
  76        },
  77};
  78
  79static struct resource msic_audio_resources[] = {
  80        {
  81                .name           = "IRQ",
  82                .flags          = IORESOURCE_IRQ,
  83        },
  84        /*
  85         * We will pass IRQ_BASE to the driver now but this can be removed
  86         * when/if the driver starts to use intel_msic_irq_read().
  87         */
  88        {
  89                .name           = "IRQ_BASE",
  90                .flags          = IORESOURCE_MEM,
  91                .start          = MSIC_IRQ_STATUS_ACCDET,
  92                .end            = MSIC_IRQ_STATUS_ACCDET,
  93        },
  94};
  95
  96static struct resource msic_hdmi_resources[] = {
  97        {
  98                .flags          = IORESOURCE_IRQ,
  99        },
 100};
 101
 102static struct resource msic_thermal_resources[] = {
 103        {
 104                .flags          = IORESOURCE_IRQ,
 105        },
 106};
 107
 108static struct resource msic_power_btn_resources[] = {
 109        {
 110                .flags          = IORESOURCE_IRQ,
 111        },
 112};
 113
 114static struct resource msic_ocd_resources[] = {
 115        {
 116                .flags          = IORESOURCE_IRQ,
 117        },
 118};
 119
 120/*
 121 * Devices that are part of the MSIC and are available via firmware
 122 * populated SFI DEVS table.
 123 */
 124static struct mfd_cell msic_devs[] = {
 125        [INTEL_MSIC_BLOCK_TOUCH]        = {
 126                .name                   = "msic_touch",
 127                .num_resources          = ARRAY_SIZE(msic_touch_resources),
 128                .resources              = msic_touch_resources,
 129        },
 130        [INTEL_MSIC_BLOCK_ADC]          = {
 131                .name                   = "msic_adc",
 132                .num_resources          = ARRAY_SIZE(msic_adc_resources),
 133                .resources              = msic_adc_resources,
 134        },
 135        [INTEL_MSIC_BLOCK_BATTERY]      = {
 136                .name                   = "msic_battery",
 137                .num_resources          = ARRAY_SIZE(msic_battery_resources),
 138                .resources              = msic_battery_resources,
 139        },
 140        [INTEL_MSIC_BLOCK_GPIO]         = {
 141                .name                   = "msic_gpio",
 142                .num_resources          = ARRAY_SIZE(msic_gpio_resources),
 143                .resources              = msic_gpio_resources,
 144        },
 145        [INTEL_MSIC_BLOCK_AUDIO]        = {
 146                .name                   = "msic_audio",
 147                .num_resources          = ARRAY_SIZE(msic_audio_resources),
 148                .resources              = msic_audio_resources,
 149        },
 150        [INTEL_MSIC_BLOCK_HDMI]         = {
 151                .name                   = "msic_hdmi",
 152                .num_resources          = ARRAY_SIZE(msic_hdmi_resources),
 153                .resources              = msic_hdmi_resources,
 154        },
 155        [INTEL_MSIC_BLOCK_THERMAL]      = {
 156                .name                   = "msic_thermal",
 157                .num_resources          = ARRAY_SIZE(msic_thermal_resources),
 158                .resources              = msic_thermal_resources,
 159        },
 160        [INTEL_MSIC_BLOCK_POWER_BTN]    = {
 161                .name                   = "msic_power_btn",
 162                .num_resources          = ARRAY_SIZE(msic_power_btn_resources),
 163                .resources              = msic_power_btn_resources,
 164        },
 165        [INTEL_MSIC_BLOCK_OCD]          = {
 166                .name                   = "msic_ocd",
 167                .num_resources          = ARRAY_SIZE(msic_ocd_resources),
 168                .resources              = msic_ocd_resources,
 169        },
 170};
 171
 172/*
 173 * Other MSIC related devices which are not directly available via SFI DEVS
 174 * table. These can be pseudo devices, regulators etc. which are needed for
 175 * different purposes.
 176 *
 177 * These devices appear only after the MSIC driver itself is initialized so
 178 * we can guarantee that the SCU IPC interface is ready.
 179 */
 180static struct mfd_cell msic_other_devs[] = {
 181        /* Audio codec in the MSIC */
 182        {
 183                .id                     = -1,
 184                .name                   = "sn95031",
 185        },
 186};
 187
 188/**
 189 * intel_msic_reg_read - read a single MSIC register
 190 * @reg: register to read
 191 * @val: register value is placed here
 192 *
 193 * Read a single register from MSIC. Returns %0 on success and negative
 194 * errno in case of failure.
 195 *
 196 * Function may sleep.
 197 */
 198int intel_msic_reg_read(unsigned short reg, u8 *val)
 199{
 200        return intel_scu_ipc_ioread8(reg, val);
 201}
 202EXPORT_SYMBOL_GPL(intel_msic_reg_read);
 203
 204/**
 205 * intel_msic_reg_write - write a single MSIC register
 206 * @reg: register to write
 207 * @val: value to write to that register
 208 *
 209 * Write a single MSIC register. Returns 0 on success and negative
 210 * errno in case of failure.
 211 *
 212 * Function may sleep.
 213 */
 214int intel_msic_reg_write(unsigned short reg, u8 val)
 215{
 216        return intel_scu_ipc_iowrite8(reg, val);
 217}
 218EXPORT_SYMBOL_GPL(intel_msic_reg_write);
 219
 220/**
 221 * intel_msic_reg_update - update a single MSIC register
 222 * @reg: register to update
 223 * @val: value to write to the register
 224 * @mask: specifies which of the bits are updated (%0 = don't update,
 225 *        %1 = update)
 226 *
 227 * Perform an update to a register @reg. @mask is used to specify which
 228 * bits are updated. Returns %0 in case of success and negative errno in
 229 * case of failure.
 230 *
 231 * Function may sleep.
 232 */
 233int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask)
 234{
 235        return intel_scu_ipc_update_register(reg, val, mask);
 236}
 237EXPORT_SYMBOL_GPL(intel_msic_reg_update);
 238
 239/**
 240 * intel_msic_bulk_read - read an array of registers
 241 * @reg: array of register addresses to read
 242 * @buf: array where the read values are placed
 243 * @count: number of registers to read
 244 *
 245 * Function reads @count registers from the MSIC using addresses passed in
 246 * @reg. Read values are placed in @buf. Reads are performed atomically
 247 * wrt. MSIC.
 248 *
 249 * Returns %0 in case of success and negative errno in case of failure.
 250 *
 251 * Function may sleep.
 252 */
 253int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
 254{
 255        if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
 256                return -EINVAL;
 257
 258        return intel_scu_ipc_readv(reg, buf, count);
 259}
 260EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
 261
 262/**
 263 * intel_msic_bulk_write - write an array of values to the MSIC registers
 264 * @reg: array of registers to write
 265 * @buf: values to write to each register
 266 * @count: number of registers to write
 267 *
 268 * Function writes @count registers in @buf to MSIC. Writes are performed
 269 * atomically wrt MSIC. Returns %0 in case of success and negative errno in
 270 * case of failure.
 271 *
 272 * Function may sleep.
 273 */
 274int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
 275{
 276        if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
 277                return -EINVAL;
 278
 279        return intel_scu_ipc_writev(reg, buf, count);
 280}
 281EXPORT_SYMBOL_GPL(intel_msic_bulk_write);
 282
 283/**
 284 * intel_msic_irq_read - read a register from an MSIC interrupt tree
 285 * @msic: MSIC instance
 286 * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and
 287 *       %INTEL_MSIC_RESETIRQ2)
 288 * @val: value of the register is placed here
 289 *
 290 * This function can be used by an MSIC subdevice interrupt handler to read
 291 * a register value from the MSIC interrupt tree. In this way subdevice
 292 * drivers don't have to map in the interrupt tree themselves but can just
 293 * call this function instead.
 294 *
 295 * Function doesn't sleep and is callable from interrupt context.
 296 *
 297 * Returns %-EINVAL if @reg is outside of the allowed register region.
 298 */
 299int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
 300{
 301        if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2))
 302                return -EINVAL;
 303
 304        *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1));
 305        return 0;
 306}
 307EXPORT_SYMBOL_GPL(intel_msic_irq_read);
 308
 309static int __devinit intel_msic_init_devices(struct intel_msic *msic)
 310{
 311        struct platform_device *pdev = msic->pdev;
 312        struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
 313        int ret, i;
 314
 315        if (pdata->gpio) {
 316                struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
 317
 318                cell->platform_data = pdata->gpio;
 319                cell->pdata_size = sizeof(*pdata->gpio);
 320        }
 321
 322        if (pdata->ocd) {
 323                unsigned gpio = pdata->ocd->gpio;
 324
 325                ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
 326                if (ret) {
 327                        dev_err(&pdev->dev, "failed to register OCD GPIO\n");
 328                        return ret;
 329                }
 330
 331                ret = gpio_to_irq(gpio);
 332                if (ret < 0) {
 333                        dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
 334                        gpio_free(gpio);
 335                        return ret;
 336                }
 337
 338                /* Update the IRQ number for the OCD */
 339                pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
 340        }
 341
 342        for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
 343                if (!pdata->irq[i])
 344                        continue;
 345
 346                ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
 347                                      pdata->irq[i], NULL);
 348                if (ret)
 349                        goto fail;
 350        }
 351
 352        ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
 353                              ARRAY_SIZE(msic_other_devs), NULL, 0, NULL);
 354        if (ret)
 355                goto fail;
 356
 357        return 0;
 358
 359fail:
 360        mfd_remove_devices(&pdev->dev);
 361        if (pdata->ocd)
 362                gpio_free(pdata->ocd->gpio);
 363
 364        return ret;
 365}
 366
 367static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
 368{
 369        struct platform_device *pdev = msic->pdev;
 370        struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
 371
 372        mfd_remove_devices(&pdev->dev);
 373
 374        if (pdata->ocd)
 375                gpio_free(pdata->ocd->gpio);
 376}
 377
 378static int __devinit intel_msic_probe(struct platform_device *pdev)
 379{
 380        struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
 381        struct intel_msic *msic;
 382        struct resource *res;
 383        u8 id0, id1;
 384        int ret;
 385
 386        if (!pdata) {
 387                dev_err(&pdev->dev, "no platform data passed\n");
 388                return -EINVAL;
 389        }
 390
 391        /* First validate that we have an MSIC in place */
 392        ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0);
 393        if (ret) {
 394                dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n");
 395                return -ENXIO;
 396        }
 397
 398        ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1);
 399        if (ret) {
 400                dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n");
 401                return -ENXIO;
 402        }
 403
 404        if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) {
 405                dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1);
 406                return -ENXIO;
 407        }
 408
 409        msic = devm_kzalloc(&pdev->dev, sizeof(*msic), GFP_KERNEL);
 410        if (!msic)
 411                return -ENOMEM;
 412
 413        msic->vendor = MSIC_VENDOR(id0);
 414        msic->version = MSIC_VERSION(id0);
 415        msic->pdev = pdev;
 416
 417        /*
 418         * Map in the MSIC interrupt tree area in SRAM. This is exposed to
 419         * the clients via intel_msic_irq_read().
 420         */
 421        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 422        if (!res) {
 423                dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
 424                return -ENODEV;
 425        }
 426
 427        msic->irq_base = devm_request_and_ioremap(&pdev->dev, res);
 428        if (!msic->irq_base) {
 429                dev_err(&pdev->dev, "failed to map SRAM memory\n");
 430                return -ENOMEM;
 431        }
 432
 433        platform_set_drvdata(pdev, msic);
 434
 435        ret = intel_msic_init_devices(msic);
 436        if (ret) {
 437                dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
 438                return ret;
 439        }
 440
 441        dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
 442                 MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version),
 443                 msic->vendor);
 444
 445        return 0;
 446}
 447
 448static int __devexit intel_msic_remove(struct platform_device *pdev)
 449{
 450        struct intel_msic *msic = platform_get_drvdata(pdev);
 451
 452        intel_msic_remove_devices(msic);
 453        platform_set_drvdata(pdev, NULL);
 454
 455        return 0;
 456}
 457
 458static struct platform_driver intel_msic_driver = {
 459        .probe          = intel_msic_probe,
 460        .remove         = __devexit_p(intel_msic_remove),
 461        .driver         = {
 462                .name   = "intel_msic",
 463                .owner  = THIS_MODULE,
 464        },
 465};
 466
 467module_platform_driver(intel_msic_driver);
 468
 469MODULE_DESCRIPTION("Driver for Intel MSIC");
 470MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
 471MODULE_LICENSE("GPL");
 472
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.