162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AMD MP2 PCIe communication driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
662306a36Sopenharmony_ci *          Elie Morisse <syniurge@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "i2c-amd-mp2.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic void amd_mp2_c2p_mutex_lock(struct amd_i2c_common *i2c_common)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	/* there is only one data mailbox for two i2c adapters */
2462306a36Sopenharmony_ci	mutex_lock(&privdata->c2p_lock);
2562306a36Sopenharmony_ci	privdata->c2p_lock_busid = i2c_common->bus_id;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
3362306a36Sopenharmony_ci		pci_warn(privdata->pci_dev,
3462306a36Sopenharmony_ci			 "bus %d attempting to unlock C2P locked by bus %d\n",
3562306a36Sopenharmony_ci			 i2c_common->bus_id, privdata->c2p_lock_busid);
3662306a36Sopenharmony_ci		return;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	mutex_unlock(&privdata->c2p_lock);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int amd_mp2_cmd(struct amd_i2c_common *i2c_common,
4362306a36Sopenharmony_ci		       union i2c_cmd_base i2c_cmd_base)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
4662306a36Sopenharmony_ci	void __iomem *reg;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	i2c_common->reqcmd = i2c_cmd_base.s.i2c_cmd;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	reg = privdata->mmio + ((i2c_cmd_base.s.bus_id == 1) ?
5162306a36Sopenharmony_ci				AMD_C2P_MSG1 : AMD_C2P_MSG0);
5262306a36Sopenharmony_ci	writel(i2c_cmd_base.ul, reg);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ciint amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
6062306a36Sopenharmony_ci	union i2c_cmd_base i2c_cmd_base;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	pci_dbg(privdata->pci_dev, "id: %d\n", i2c_common->bus_id);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	i2c_cmd_base.ul = 0;
6562306a36Sopenharmony_ci	i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
6662306a36Sopenharmony_ci	i2c_cmd_base.s.bus_id = i2c_common->bus_id;
6762306a36Sopenharmony_ci	i2c_cmd_base.s.i2c_speed = i2c_common->i2c_speed;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	amd_mp2_c2p_mutex_lock(i2c_common);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return amd_mp2_cmd(i2c_common, i2c_cmd_base);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_bus_enable_set);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void amd_mp2_cmd_rw_fill(struct amd_i2c_common *i2c_common,
7662306a36Sopenharmony_ci				union i2c_cmd_base *i2c_cmd_base,
7762306a36Sopenharmony_ci				enum i2c_cmd reqcmd)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	i2c_cmd_base->s.i2c_cmd = reqcmd;
8062306a36Sopenharmony_ci	i2c_cmd_base->s.bus_id = i2c_common->bus_id;
8162306a36Sopenharmony_ci	i2c_cmd_base->s.i2c_speed = i2c_common->i2c_speed;
8262306a36Sopenharmony_ci	i2c_cmd_base->s.slave_addr = i2c_common->msg->addr;
8362306a36Sopenharmony_ci	i2c_cmd_base->s.length = i2c_common->msg->len;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
8962306a36Sopenharmony_ci	union i2c_cmd_base i2c_cmd_base;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	amd_mp2_cmd_rw_fill(i2c_common, &i2c_cmd_base, reqcmd);
9262306a36Sopenharmony_ci	amd_mp2_c2p_mutex_lock(i2c_common);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (i2c_common->msg->len <= 32) {
9562306a36Sopenharmony_ci		i2c_cmd_base.s.mem_type = use_c2pmsg;
9662306a36Sopenharmony_ci		if (reqcmd == i2c_write)
9762306a36Sopenharmony_ci			memcpy_toio(privdata->mmio + AMD_C2P_MSG2,
9862306a36Sopenharmony_ci				    i2c_common->msg->buf,
9962306a36Sopenharmony_ci				    i2c_common->msg->len);
10062306a36Sopenharmony_ci	} else {
10162306a36Sopenharmony_ci		i2c_cmd_base.s.mem_type = use_dram;
10262306a36Sopenharmony_ci		writeq((u64)i2c_common->dma_addr,
10362306a36Sopenharmony_ci		       privdata->mmio + AMD_C2P_MSG2);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return amd_mp2_cmd(i2c_common, i2c_cmd_base);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_rw);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
11362306a36Sopenharmony_ci	struct pci_dev *pdev = privdata->pci_dev;
11462306a36Sopenharmony_ci	int len = i2c_common->eventval.r.length;
11562306a36Sopenharmony_ci	u32 slave_addr = i2c_common->eventval.r.slave_addr;
11662306a36Sopenharmony_ci	bool err = false;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (unlikely(len != i2c_common->msg->len)) {
11962306a36Sopenharmony_ci		pci_err(pdev, "length %d in event doesn't match buffer length %d!\n",
12062306a36Sopenharmony_ci			len, i2c_common->msg->len);
12162306a36Sopenharmony_ci		err = true;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (unlikely(slave_addr != i2c_common->msg->addr)) {
12562306a36Sopenharmony_ci		pci_err(pdev, "unexpected slave address %x (expected: %x)!\n",
12662306a36Sopenharmony_ci			slave_addr, i2c_common->msg->addr);
12762306a36Sopenharmony_ci		err = true;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!err)
13162306a36Sopenharmony_ci		i2c_common->cmd_success = true;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
13762306a36Sopenharmony_ci	struct pci_dev *pdev = privdata->pci_dev;
13862306a36Sopenharmony_ci	enum status_type sts = i2c_common->eventval.r.status;
13962306a36Sopenharmony_ci	enum response_type res = i2c_common->eventval.r.response;
14062306a36Sopenharmony_ci	int len = i2c_common->eventval.r.length;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (res != command_success) {
14362306a36Sopenharmony_ci		if (res != command_failed)
14462306a36Sopenharmony_ci			pci_err(pdev, "invalid response to i2c command!\n");
14562306a36Sopenharmony_ci		return;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	switch (i2c_common->reqcmd) {
14962306a36Sopenharmony_ci	case i2c_read:
15062306a36Sopenharmony_ci		if (sts == i2c_readcomplete_event) {
15162306a36Sopenharmony_ci			amd_mp2_pci_check_rw_event(i2c_common);
15262306a36Sopenharmony_ci			if (len <= 32)
15362306a36Sopenharmony_ci				memcpy_fromio(i2c_common->msg->buf,
15462306a36Sopenharmony_ci					      privdata->mmio + AMD_C2P_MSG2,
15562306a36Sopenharmony_ci					      len);
15662306a36Sopenharmony_ci		} else if (sts != i2c_readfail_event) {
15762306a36Sopenharmony_ci			pci_err(pdev, "invalid i2c status after read (%d)!\n", sts);
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case i2c_write:
16162306a36Sopenharmony_ci		if (sts == i2c_writecomplete_event)
16262306a36Sopenharmony_ci			amd_mp2_pci_check_rw_event(i2c_common);
16362306a36Sopenharmony_ci		else if (sts != i2c_writefail_event)
16462306a36Sopenharmony_ci			pci_err(pdev, "invalid i2c status after write (%d)!\n", sts);
16562306a36Sopenharmony_ci		break;
16662306a36Sopenharmony_ci	case i2c_enable:
16762306a36Sopenharmony_ci		if (sts == i2c_busenable_complete)
16862306a36Sopenharmony_ci			i2c_common->cmd_success = true;
16962306a36Sopenharmony_ci		else if (sts != i2c_busenable_failed)
17062306a36Sopenharmony_ci			pci_err(pdev, "invalid i2c status after bus enable (%d)!\n", sts);
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	case i2c_disable:
17362306a36Sopenharmony_ci		if (sts == i2c_busdisable_complete)
17462306a36Sopenharmony_ci			i2c_common->cmd_success = true;
17562306a36Sopenharmony_ci		else if (sts != i2c_busdisable_failed)
17662306a36Sopenharmony_ci			pci_err(pdev, "invalid i2c status after bus disable (%d)!\n", sts);
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	default:
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid amd_mp2_process_event(struct amd_i2c_common *i2c_common)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
18662306a36Sopenharmony_ci	struct pci_dev *pdev = privdata->pci_dev;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (unlikely(i2c_common->reqcmd == i2c_none)) {
18962306a36Sopenharmony_ci		pci_warn(pdev, "received msg but no cmd was sent (bus = %d)!\n",
19062306a36Sopenharmony_ci			 i2c_common->bus_id);
19162306a36Sopenharmony_ci		return;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	__amd_mp2_process_event(i2c_common);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	i2c_common->reqcmd = i2c_none;
19762306a36Sopenharmony_ci	amd_mp2_c2p_mutex_unlock(i2c_common);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_process_event);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = dev;
20462306a36Sopenharmony_ci	struct pci_dev *pdev = privdata->pci_dev;
20562306a36Sopenharmony_ci	struct amd_i2c_common *i2c_common;
20662306a36Sopenharmony_ci	u32 val;
20762306a36Sopenharmony_ci	unsigned int bus_id;
20862306a36Sopenharmony_ci	void __iomem *reg;
20962306a36Sopenharmony_ci	enum irqreturn ret = IRQ_NONE;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for (bus_id = 0; bus_id < 2; bus_id++) {
21262306a36Sopenharmony_ci		i2c_common = privdata->busses[bus_id];
21362306a36Sopenharmony_ci		if (!i2c_common)
21462306a36Sopenharmony_ci			continue;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		reg = privdata->mmio + ((bus_id == 0) ?
21762306a36Sopenharmony_ci					AMD_P2C_MSG1 : AMD_P2C_MSG2);
21862306a36Sopenharmony_ci		val = readl(reg);
21962306a36Sopenharmony_ci		if (val != 0) {
22062306a36Sopenharmony_ci			writel(0, reg);
22162306a36Sopenharmony_ci			writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
22262306a36Sopenharmony_ci			i2c_common->eventval.ul = val;
22362306a36Sopenharmony_ci			i2c_common->cmd_completion(i2c_common);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci			ret = IRQ_HANDLED;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (ret != IRQ_HANDLED) {
23062306a36Sopenharmony_ci		val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
23162306a36Sopenharmony_ci		if (val != 0) {
23262306a36Sopenharmony_ci			writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
23362306a36Sopenharmony_ci			pci_warn(pdev, "received irq without message\n");
23462306a36Sopenharmony_ci			ret = IRQ_HANDLED;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	i2c_common->reqcmd = i2c_none;
24462306a36Sopenharmony_ci	amd_mp2_c2p_mutex_unlock(i2c_common);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciint amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
25162306a36Sopenharmony_ci	struct pci_dev *pdev = privdata->pci_dev;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (i2c_common->bus_id > 1)
25462306a36Sopenharmony_ci		return -EINVAL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (privdata->busses[i2c_common->bus_id]) {
25762306a36Sopenharmony_ci		pci_err(pdev, "Bus %d already taken!\n", i2c_common->bus_id);
25862306a36Sopenharmony_ci		return -EINVAL;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	privdata->busses[i2c_common->bus_id] = i2c_common;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_register_cb);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ciint amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	privdata->busses[i2c_common->bus_id] = NULL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_unregister_cb);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void amd_mp2_clear_reg(struct amd_mp2_dev *privdata)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int reg;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	for (reg = AMD_C2P_MSG0; reg <= AMD_C2P_MSG9; reg += 4)
28262306a36Sopenharmony_ci		writel(0, privdata->mmio + reg);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	for (reg = AMD_P2C_MSG1; reg <= AMD_P2C_MSG2; reg += 4)
28562306a36Sopenharmony_ci		writel(0, privdata->mmio + reg);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
28962306a36Sopenharmony_ci			    struct pci_dev *pci_dev)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	int irq_flag = 0, rc;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, privdata);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	rc = pcim_enable_device(pci_dev);
29662306a36Sopenharmony_ci	if (rc) {
29762306a36Sopenharmony_ci		pci_err(pci_dev, "Failed to enable MP2 PCI device\n");
29862306a36Sopenharmony_ci		goto err_pci_enable;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
30262306a36Sopenharmony_ci	if (rc) {
30362306a36Sopenharmony_ci		pci_err(pci_dev, "I/O memory remapping failed\n");
30462306a36Sopenharmony_ci		goto err_pci_enable;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	privdata->mmio = pcim_iomap_table(pci_dev)[2];
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pci_set_master(pci_dev);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
31162306a36Sopenharmony_ci	if (rc)
31262306a36Sopenharmony_ci		goto err_dma_mask;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* request and enable interrupt */
31562306a36Sopenharmony_ci	writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
31662306a36Sopenharmony_ci	rc = pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_ALL_TYPES);
31762306a36Sopenharmony_ci	if (rc < 0) {
31862306a36Sopenharmony_ci		dev_err(&pci_dev->dev, "Failed to allocate single IRQ err=%d\n", rc);
31962306a36Sopenharmony_ci		goto err_dma_mask;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	privdata->dev_irq = pci_irq_vector(pci_dev, 0);
32362306a36Sopenharmony_ci	if (!pci_dev->msix_enabled && !pci_dev->msi_enabled)
32462306a36Sopenharmony_ci		irq_flag = IRQF_SHARED;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	rc = devm_request_irq(&pci_dev->dev, privdata->dev_irq,
32762306a36Sopenharmony_ci			      amd_mp2_irq_isr, irq_flag, dev_name(&pci_dev->dev), privdata);
32862306a36Sopenharmony_ci	if (rc) {
32962306a36Sopenharmony_ci		pci_err(pci_dev, "Failure requesting irq %i: %d\n", privdata->dev_irq, rc);
33062306a36Sopenharmony_ci		goto free_irq_vectors;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return rc;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cifree_irq_vectors:
33662306a36Sopenharmony_ci	free_irq(privdata->dev_irq, privdata);
33762306a36Sopenharmony_cierr_dma_mask:
33862306a36Sopenharmony_ci	pci_clear_master(pci_dev);
33962306a36Sopenharmony_cierr_pci_enable:
34062306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, NULL);
34162306a36Sopenharmony_ci	return rc;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int amd_mp2_pci_probe(struct pci_dev *pci_dev,
34562306a36Sopenharmony_ci			     const struct pci_device_id *id)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct amd_mp2_dev *privdata;
34862306a36Sopenharmony_ci	int rc;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	privdata = devm_kzalloc(&pci_dev->dev, sizeof(*privdata), GFP_KERNEL);
35162306a36Sopenharmony_ci	if (!privdata)
35262306a36Sopenharmony_ci		return -ENOMEM;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	privdata->pci_dev = pci_dev;
35562306a36Sopenharmony_ci	rc = amd_mp2_pci_init(privdata, pci_dev);
35662306a36Sopenharmony_ci	if (rc)
35762306a36Sopenharmony_ci		return rc;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	mutex_init(&privdata->c2p_lock);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
36262306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pci_dev->dev);
36362306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pci_dev->dev);
36462306a36Sopenharmony_ci	pm_runtime_allow(&pci_dev->dev);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	privdata->probed = true;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	pci_info(pci_dev, "MP2 device registered.\n");
36962306a36Sopenharmony_ci	return 0;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void amd_mp2_pci_remove(struct pci_dev *pci_dev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pm_runtime_forbid(&pci_dev->dev);
37762306a36Sopenharmony_ci	pm_runtime_get_noresume(&pci_dev->dev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	free_irq(privdata->dev_irq, privdata);
38062306a36Sopenharmony_ci	pci_clear_master(pci_dev);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	amd_mp2_clear_reg(privdata);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci#ifdef CONFIG_PM
38662306a36Sopenharmony_cistatic int amd_mp2_pci_suspend(struct device *dev)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
38962306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
39062306a36Sopenharmony_ci	struct amd_i2c_common *i2c_common;
39162306a36Sopenharmony_ci	unsigned int bus_id;
39262306a36Sopenharmony_ci	int ret = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	for (bus_id = 0; bus_id < 2; bus_id++) {
39562306a36Sopenharmony_ci		i2c_common = privdata->busses[bus_id];
39662306a36Sopenharmony_ci		if (i2c_common)
39762306a36Sopenharmony_ci			i2c_common->suspend(i2c_common);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = pci_save_state(pci_dev);
40162306a36Sopenharmony_ci	if (ret) {
40262306a36Sopenharmony_ci		pci_err(pci_dev, "pci_save_state failed = %d\n", ret);
40362306a36Sopenharmony_ci		return ret;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	pci_disable_device(pci_dev);
40762306a36Sopenharmony_ci	return ret;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int amd_mp2_pci_resume(struct device *dev)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
41362306a36Sopenharmony_ci	struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
41462306a36Sopenharmony_ci	struct amd_i2c_common *i2c_common;
41562306a36Sopenharmony_ci	unsigned int bus_id;
41662306a36Sopenharmony_ci	int ret = 0;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	pci_restore_state(pci_dev);
41962306a36Sopenharmony_ci	ret = pci_enable_device(pci_dev);
42062306a36Sopenharmony_ci	if (ret < 0) {
42162306a36Sopenharmony_ci		pci_err(pci_dev, "pci_enable_device failed = %d\n", ret);
42262306a36Sopenharmony_ci		return ret;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	for (bus_id = 0; bus_id < 2; bus_id++) {
42662306a36Sopenharmony_ci		i2c_common = privdata->busses[bus_id];
42762306a36Sopenharmony_ci		if (i2c_common) {
42862306a36Sopenharmony_ci			ret = i2c_common->resume(i2c_common);
42962306a36Sopenharmony_ci			if (ret < 0)
43062306a36Sopenharmony_ci				return ret;
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic UNIVERSAL_DEV_PM_OPS(amd_mp2_pci_pm_ops, amd_mp2_pci_suspend,
43862306a36Sopenharmony_ci			    amd_mp2_pci_resume, NULL);
43962306a36Sopenharmony_ci#endif /* CONFIG_PM */
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic const struct pci_device_id amd_mp2_pci_tbl[] = {
44262306a36Sopenharmony_ci	{PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2)},
44362306a36Sopenharmony_ci	{0}
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic struct pci_driver amd_mp2_pci_driver = {
44862306a36Sopenharmony_ci	.name		= "i2c_amd_mp2",
44962306a36Sopenharmony_ci	.id_table	= amd_mp2_pci_tbl,
45062306a36Sopenharmony_ci	.probe		= amd_mp2_pci_probe,
45162306a36Sopenharmony_ci	.remove		= amd_mp2_pci_remove,
45262306a36Sopenharmony_ci#ifdef CONFIG_PM
45362306a36Sopenharmony_ci	.driver = {
45462306a36Sopenharmony_ci		.pm	= &amd_mp2_pci_pm_ops,
45562306a36Sopenharmony_ci	},
45662306a36Sopenharmony_ci#endif
45762306a36Sopenharmony_ci};
45862306a36Sopenharmony_cimodule_pci_driver(amd_mp2_pci_driver);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistruct amd_mp2_dev *amd_mp2_find_device(void)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct device *dev;
46362306a36Sopenharmony_ci	struct pci_dev *pci_dev;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	dev = driver_find_next_device(&amd_mp2_pci_driver.driver, NULL);
46662306a36Sopenharmony_ci	if (!dev)
46762306a36Sopenharmony_ci		return NULL;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	pci_dev = to_pci_dev(dev);
47062306a36Sopenharmony_ci	return (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_mp2_find_device);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver");
47562306a36Sopenharmony_ciMODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
47662306a36Sopenharmony_ciMODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
47762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
478