linux/drivers/i2c/busses/i2c-designware-baytrail.c
<<
>>
Prefs
   1/*
   2 * Intel BayTrail PMIC I2C bus semaphore implementaion
   3 * Copyright (c) 2014, Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 */
  14#include <linux/module.h>
  15#include <linux/delay.h>
  16#include <linux/device.h>
  17#include <linux/acpi.h>
  18#include <linux/i2c.h>
  19#include <linux/interrupt.h>
  20
  21#include <asm/iosf_mbi.h>
  22
  23#include "i2c-designware-core.h"
  24
  25#define SEMAPHORE_TIMEOUT       100
  26#define PUNIT_SEMAPHORE         0x7
  27#define PUNIT_SEMAPHORE_BIT     BIT(0)
  28#define PUNIT_SEMAPHORE_ACQUIRE BIT(1)
  29
  30static unsigned long acquired;
  31
  32static int get_sem(struct device *dev, u32 *sem)
  33{
  34        u32 data;
  35        int ret;
  36
  37        ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE,
  38                                &data);
  39        if (ret) {
  40                dev_err(dev, "iosf failed to read punit semaphore\n");
  41                return ret;
  42        }
  43
  44        *sem = data & PUNIT_SEMAPHORE_BIT;
  45
  46        return 0;
  47}
  48
  49static void reset_semaphore(struct device *dev)
  50{
  51        u32 data;
  52
  53        if (iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
  54                                PUNIT_SEMAPHORE, &data)) {
  55                dev_err(dev, "iosf failed to reset punit semaphore during read\n");
  56                return;
  57        }
  58
  59        data &= ~PUNIT_SEMAPHORE_BIT;
  60        if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
  61                                PUNIT_SEMAPHORE, data))
  62                dev_err(dev, "iosf failed to reset punit semaphore during write\n");
  63}
  64
  65static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
  66{
  67        u32 sem;
  68        int ret;
  69        unsigned long start, end;
  70
  71        might_sleep();
  72
  73        if (!dev || !dev->dev)
  74                return -ENODEV;
  75
  76        if (!dev->release_lock)
  77                return 0;
  78
  79        /* host driver writes to side band semaphore register */
  80        ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
  81                                PUNIT_SEMAPHORE, PUNIT_SEMAPHORE_ACQUIRE);
  82        if (ret) {
  83                dev_err(dev->dev, "iosf punit semaphore request failed\n");
  84                return ret;
  85        }
  86
  87        /* host driver waits for bit 0 to be set in semaphore register */
  88        start = jiffies;
  89        end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
  90        do {
  91                ret = get_sem(dev->dev, &sem);
  92                if (!ret && sem) {
  93                        acquired = jiffies;
  94                        dev_dbg(dev->dev, "punit semaphore acquired after %ums\n",
  95                                jiffies_to_msecs(jiffies - start));
  96                        return 0;
  97                }
  98
  99                usleep_range(1000, 2000);
 100        } while (time_before(jiffies, end));
 101
 102        dev_err(dev->dev, "punit semaphore timed out, resetting\n");
 103        reset_semaphore(dev->dev);
 104
 105        ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
 106                                PUNIT_SEMAPHORE, &sem);
 107        if (ret)
 108                dev_err(dev->dev, "iosf failed to read punit semaphore\n");
 109        else
 110                dev_err(dev->dev, "PUNIT SEM: %d\n", sem);
 111
 112        WARN_ON(1);
 113
 114        return -ETIMEDOUT;
 115}
 116
 117static void baytrail_i2c_release(struct dw_i2c_dev *dev)
 118{
 119        if (!dev || !dev->dev)
 120                return;
 121
 122        if (!dev->acquire_lock)
 123                return;
 124
 125        reset_semaphore(dev->dev);
 126        dev_dbg(dev->dev, "punit semaphore held for %ums\n",
 127                jiffies_to_msecs(jiffies - acquired));
 128}
 129
 130int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev)
 131{
 132        acpi_status status;
 133        unsigned long long shared_host = 0;
 134        acpi_handle handle;
 135
 136        if (!dev || !dev->dev)
 137                return 0;
 138
 139        handle = ACPI_HANDLE(dev->dev);
 140        if (!handle)
 141                return 0;
 142
 143        status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
 144        if (ACPI_FAILURE(status))
 145                return 0;
 146
 147        if (shared_host) {
 148                dev_info(dev->dev, "I2C bus managed by PUNIT\n");
 149                dev->acquire_lock = baytrail_i2c_acquire;
 150                dev->release_lock = baytrail_i2c_release;
 151                dev->pm_runtime_disabled = true;
 152        }
 153
 154        if (!iosf_mbi_available())
 155                return -EPROBE_DEFER;
 156
 157        return 0;
 158}
 159
 160MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
 161MODULE_DESCRIPTION("Baytrail I2C Semaphore driver");
 162MODULE_LICENSE("GPL v2");
 163