cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

oaktrail_hdmi_i2c.c (8388B)


      1/*
      2 * Copyright © 2010 Intel Corporation
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice (including the next
     12 * paragraph) shall be included in all copies or substantial portions of the
     13 * Software.
     14 *
     15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21 * DEALINGS IN THE SOFTWARE.
     22 *
     23 * Authors:
     24 *	Li Peng <peng.li@intel.com>
     25 */
     26
     27#include <linux/export.h>
     28#include <linux/mutex.h>
     29#include <linux/pci.h>
     30#include <linux/i2c.h>
     31#include <linux/interrupt.h>
     32#include <linux/delay.h>
     33#include "psb_drv.h"
     34
     35#define HDMI_READ(reg)		readl(hdmi_dev->regs + (reg))
     36#define HDMI_WRITE(reg, val)	writel(val, hdmi_dev->regs + (reg))
     37
     38#define HDMI_HCR	0x1000
     39#define HCR_DETECT_HDP		(1 << 6)
     40#define HCR_ENABLE_HDCP		(1 << 5)
     41#define HCR_ENABLE_AUDIO	(1 << 2)
     42#define HCR_ENABLE_PIXEL	(1 << 1)
     43#define HCR_ENABLE_TMDS		(1 << 0)
     44#define HDMI_HICR	0x1004
     45#define HDMI_INTR_I2C_ERROR	(1 << 4)
     46#define HDMI_INTR_I2C_FULL	(1 << 3)
     47#define HDMI_INTR_I2C_DONE	(1 << 2)
     48#define HDMI_INTR_HPD		(1 << 0)
     49#define HDMI_HSR	0x1008
     50#define HDMI_HISR	0x100C
     51#define HDMI_HI2CRDB0	0x1200
     52#define HDMI_HI2CHCR	0x1240
     53#define HI2C_HDCP_WRITE		(0 << 2)
     54#define HI2C_HDCP_RI_READ	(1 << 2)
     55#define HI2C_HDCP_READ		(2 << 2)
     56#define HI2C_EDID_READ		(3 << 2)
     57#define HI2C_READ_CONTINUE	(1 << 1)
     58#define HI2C_ENABLE_TRANSACTION	(1 << 0)
     59
     60#define HDMI_ICRH	0x1100
     61#define HDMI_HI2CTDR0	0x1244
     62#define HDMI_HI2CTDR1	0x1248
     63
     64#define I2C_STAT_INIT		0
     65#define I2C_READ_DONE		1
     66#define I2C_TRANSACTION_DONE	2
     67
     68struct hdmi_i2c_dev {
     69	struct i2c_adapter *adap;
     70	struct mutex i2c_lock;
     71	struct completion complete;
     72	int status;
     73	struct i2c_msg *msg;
     74	int buf_offset;
     75};
     76
     77static void hdmi_i2c_irq_enable(struct oaktrail_hdmi_dev *hdmi_dev)
     78{
     79	u32 temp;
     80
     81	temp = HDMI_READ(HDMI_HICR);
     82	temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE);
     83	HDMI_WRITE(HDMI_HICR, temp);
     84	HDMI_READ(HDMI_HICR);
     85}
     86
     87static void hdmi_i2c_irq_disable(struct oaktrail_hdmi_dev *hdmi_dev)
     88{
     89	HDMI_WRITE(HDMI_HICR, 0x0);
     90	HDMI_READ(HDMI_HICR);
     91}
     92
     93static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
     94{
     95	struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
     96	struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
     97	u32 temp;
     98
     99	i2c_dev->status = I2C_STAT_INIT;
    100	i2c_dev->msg = pmsg;
    101	i2c_dev->buf_offset = 0;
    102	reinit_completion(&i2c_dev->complete);
    103
    104	/* Enable I2C transaction */
    105	temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION;
    106	HDMI_WRITE(HDMI_HI2CHCR, temp);
    107	HDMI_READ(HDMI_HI2CHCR);
    108
    109	while (i2c_dev->status != I2C_TRANSACTION_DONE)
    110		wait_for_completion_interruptible_timeout(&i2c_dev->complete,
    111								10 * HZ);
    112
    113	return 0;
    114}
    115
    116static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg)
    117{
    118	/*
    119	 * XXX: i2c write seems isn't useful for EDID probe, don't do anything
    120	 */
    121	return 0;
    122}
    123
    124static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
    125				struct i2c_msg *pmsg,
    126				int num)
    127{
    128	struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
    129	struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
    130	int i;
    131
    132	mutex_lock(&i2c_dev->i2c_lock);
    133
    134	/* Enable i2c unit */
    135	HDMI_WRITE(HDMI_ICRH, 0x00008760);
    136
    137	/* Enable irq */
    138	hdmi_i2c_irq_enable(hdmi_dev);
    139	for (i = 0; i < num; i++) {
    140		if (pmsg->len && pmsg->buf) {
    141			if (pmsg->flags & I2C_M_RD)
    142				xfer_read(adap, pmsg);
    143			else
    144				xfer_write(adap, pmsg);
    145		}
    146		pmsg++;         /* next message */
    147	}
    148
    149	/* Disable irq */
    150	hdmi_i2c_irq_disable(hdmi_dev);
    151
    152	mutex_unlock(&i2c_dev->i2c_lock);
    153
    154	return i;
    155}
    156
    157static u32 oaktrail_hdmi_i2c_func(struct i2c_adapter *adapter)
    158{
    159	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
    160}
    161
    162static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = {
    163	.master_xfer	= oaktrail_hdmi_i2c_access,
    164	.functionality  = oaktrail_hdmi_i2c_func,
    165};
    166
    167static struct i2c_adapter oaktrail_hdmi_i2c_adapter = {
    168	.name		= "oaktrail_hdmi_i2c",
    169	.nr		= 3,
    170	.owner		= THIS_MODULE,
    171	.class		= I2C_CLASS_DDC,
    172	.algo		= &oaktrail_hdmi_i2c_algorithm,
    173};
    174
    175static void hdmi_i2c_read(struct oaktrail_hdmi_dev *hdmi_dev)
    176{
    177	struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
    178	struct i2c_msg *msg = i2c_dev->msg;
    179	u8 *buf = msg->buf;
    180	u32 temp;
    181	int i, offset;
    182
    183	offset = i2c_dev->buf_offset;
    184	for (i = 0; i < 0x10; i++) {
    185		temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4));
    186		memcpy(buf + (offset + i * 4), &temp, 4);
    187	}
    188	i2c_dev->buf_offset += (0x10 * 4);
    189
    190	/* clearing read buffer full intr */
    191	temp = HDMI_READ(HDMI_HISR);
    192	HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL);
    193	HDMI_READ(HDMI_HISR);
    194
    195	/* continue read transaction */
    196	temp = HDMI_READ(HDMI_HI2CHCR);
    197	HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE);
    198	HDMI_READ(HDMI_HI2CHCR);
    199
    200	i2c_dev->status = I2C_READ_DONE;
    201	return;
    202}
    203
    204static void hdmi_i2c_transaction_done(struct oaktrail_hdmi_dev *hdmi_dev)
    205{
    206	struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
    207	u32 temp;
    208
    209	/* clear transaction done intr */
    210	temp = HDMI_READ(HDMI_HISR);
    211	HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE);
    212	HDMI_READ(HDMI_HISR);
    213
    214
    215	temp = HDMI_READ(HDMI_HI2CHCR);
    216	HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION);
    217	HDMI_READ(HDMI_HI2CHCR);
    218
    219	i2c_dev->status = I2C_TRANSACTION_DONE;
    220	return;
    221}
    222
    223static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev)
    224{
    225	struct oaktrail_hdmi_dev *hdmi_dev = dev;
    226	struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
    227	u32 stat;
    228
    229	stat = HDMI_READ(HDMI_HISR);
    230
    231	if (stat & HDMI_INTR_HPD) {
    232		HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD);
    233		HDMI_READ(HDMI_HISR);
    234	}
    235
    236	if (stat & HDMI_INTR_I2C_FULL)
    237		hdmi_i2c_read(hdmi_dev);
    238
    239	if (stat & HDMI_INTR_I2C_DONE)
    240		hdmi_i2c_transaction_done(hdmi_dev);
    241
    242	complete(&i2c_dev->complete);
    243
    244	return IRQ_HANDLED;
    245}
    246
    247/*
    248 * choose alternate function 2 of GPIO pin 52, 53,
    249 * which is used by HDMI I2C logic
    250 */
    251static void oaktrail_hdmi_i2c_gpio_fix(void)
    252{
    253	void __iomem *base;
    254	unsigned int gpio_base = 0xff12c000;
    255	int gpio_len = 0x1000;
    256	u32 temp;
    257
    258	base = ioremap((resource_size_t)gpio_base, gpio_len);
    259	if (base == NULL) {
    260		DRM_ERROR("gpio ioremap fail\n");
    261		return;
    262	}
    263
    264	temp = readl(base + 0x44);
    265	DRM_DEBUG_DRIVER("old gpio val %x\n", temp);
    266	writel((temp | 0x00000a00), (base +  0x44));
    267	temp = readl(base + 0x44);
    268	DRM_DEBUG_DRIVER("new gpio val %x\n", temp);
    269
    270	iounmap(base);
    271}
    272
    273int oaktrail_hdmi_i2c_init(struct pci_dev *dev)
    274{
    275	struct oaktrail_hdmi_dev *hdmi_dev;
    276	struct hdmi_i2c_dev *i2c_dev;
    277	int ret;
    278
    279	hdmi_dev = pci_get_drvdata(dev);
    280
    281	i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL);
    282	if (!i2c_dev)
    283		return -ENOMEM;
    284
    285	i2c_dev->adap = &oaktrail_hdmi_i2c_adapter;
    286	i2c_dev->status = I2C_STAT_INIT;
    287	init_completion(&i2c_dev->complete);
    288	mutex_init(&i2c_dev->i2c_lock);
    289	i2c_set_adapdata(&oaktrail_hdmi_i2c_adapter, hdmi_dev);
    290	hdmi_dev->i2c_dev = i2c_dev;
    291
    292	/* Enable HDMI I2C function on gpio */
    293	oaktrail_hdmi_i2c_gpio_fix();
    294
    295	/* request irq */
    296	ret = request_irq(dev->irq, oaktrail_hdmi_i2c_handler, IRQF_SHARED,
    297			  oaktrail_hdmi_i2c_adapter.name, hdmi_dev);
    298	if (ret) {
    299		DRM_ERROR("Failed to request IRQ for I2C controller\n");
    300		goto free_dev;
    301	}
    302
    303	/* Adapter registration */
    304	ret = i2c_add_numbered_adapter(&oaktrail_hdmi_i2c_adapter);
    305	if (ret) {
    306		DRM_ERROR("Failed to add I2C adapter\n");
    307		goto free_irq;
    308	}
    309
    310	return 0;
    311
    312free_irq:
    313	free_irq(dev->irq, hdmi_dev);
    314free_dev:
    315	kfree(i2c_dev);
    316
    317	return ret;
    318}
    319
    320void oaktrail_hdmi_i2c_exit(struct pci_dev *dev)
    321{
    322	struct oaktrail_hdmi_dev *hdmi_dev;
    323	struct hdmi_i2c_dev *i2c_dev;
    324
    325	hdmi_dev = pci_get_drvdata(dev);
    326	i2c_del_adapter(&oaktrail_hdmi_i2c_adapter);
    327
    328	i2c_dev = hdmi_dev->i2c_dev;
    329	kfree(i2c_dev);
    330	free_irq(dev->irq, hdmi_dev);
    331}