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