18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/pci.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/ktime.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PCH_EVENT_SET 0 /* I2C Interrupt Event Set Status */ 228c2ecf20Sopenharmony_ci#define PCH_EVENT_NONE 1 /* I2C Interrupt Event Clear Status */ 238c2ecf20Sopenharmony_ci#define PCH_MAX_CLK 100000 /* Maximum Clock speed in MHz */ 248c2ecf20Sopenharmony_ci#define PCH_BUFFER_MODE_ENABLE 0x0002 /* flag for Buffer mode enable */ 258c2ecf20Sopenharmony_ci#define PCH_EEPROM_SW_RST_MODE_ENABLE 0x0008 /* EEPROM SW RST enable flag */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PCH_I2CSADR 0x00 /* I2C slave address register */ 288c2ecf20Sopenharmony_ci#define PCH_I2CCTL 0x04 /* I2C control register */ 298c2ecf20Sopenharmony_ci#define PCH_I2CSR 0x08 /* I2C status register */ 308c2ecf20Sopenharmony_ci#define PCH_I2CDR 0x0C /* I2C data register */ 318c2ecf20Sopenharmony_ci#define PCH_I2CMON 0x10 /* I2C bus monitor register */ 328c2ecf20Sopenharmony_ci#define PCH_I2CBC 0x14 /* I2C bus transfer rate setup counter */ 338c2ecf20Sopenharmony_ci#define PCH_I2CMOD 0x18 /* I2C mode register */ 348c2ecf20Sopenharmony_ci#define PCH_I2CBUFSLV 0x1C /* I2C buffer mode slave address register */ 358c2ecf20Sopenharmony_ci#define PCH_I2CBUFSUB 0x20 /* I2C buffer mode subaddress register */ 368c2ecf20Sopenharmony_ci#define PCH_I2CBUFFOR 0x24 /* I2C buffer mode format register */ 378c2ecf20Sopenharmony_ci#define PCH_I2CBUFCTL 0x28 /* I2C buffer mode control register */ 388c2ecf20Sopenharmony_ci#define PCH_I2CBUFMSK 0x2C /* I2C buffer mode interrupt mask register */ 398c2ecf20Sopenharmony_ci#define PCH_I2CBUFSTA 0x30 /* I2C buffer mode status register */ 408c2ecf20Sopenharmony_ci#define PCH_I2CBUFLEV 0x34 /* I2C buffer mode level register */ 418c2ecf20Sopenharmony_ci#define PCH_I2CESRFOR 0x38 /* EEPROM software reset mode format register */ 428c2ecf20Sopenharmony_ci#define PCH_I2CESRCTL 0x3C /* EEPROM software reset mode ctrl register */ 438c2ecf20Sopenharmony_ci#define PCH_I2CESRMSK 0x40 /* EEPROM software reset mode */ 448c2ecf20Sopenharmony_ci#define PCH_I2CESRSTA 0x44 /* EEPROM software reset mode status register */ 458c2ecf20Sopenharmony_ci#define PCH_I2CTMR 0x48 /* I2C timer register */ 468c2ecf20Sopenharmony_ci#define PCH_I2CSRST 0xFC /* I2C reset register */ 478c2ecf20Sopenharmony_ci#define PCH_I2CNF 0xF8 /* I2C noise filter register */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define BUS_IDLE_TIMEOUT 20 508c2ecf20Sopenharmony_ci#define PCH_I2CCTL_I2CMEN 0x0080 518c2ecf20Sopenharmony_ci#define TEN_BIT_ADDR_DEFAULT 0xF000 528c2ecf20Sopenharmony_ci#define TEN_BIT_ADDR_MASK 0xF0 538c2ecf20Sopenharmony_ci#define PCH_START 0x0020 548c2ecf20Sopenharmony_ci#define PCH_RESTART 0x0004 558c2ecf20Sopenharmony_ci#define PCH_ESR_START 0x0001 568c2ecf20Sopenharmony_ci#define PCH_BUFF_START 0x1 578c2ecf20Sopenharmony_ci#define PCH_REPSTART 0x0004 588c2ecf20Sopenharmony_ci#define PCH_ACK 0x0008 598c2ecf20Sopenharmony_ci#define PCH_GETACK 0x0001 608c2ecf20Sopenharmony_ci#define CLR_REG 0x0 618c2ecf20Sopenharmony_ci#define I2C_RD 0x1 628c2ecf20Sopenharmony_ci#define I2CMCF_BIT 0x0080 638c2ecf20Sopenharmony_ci#define I2CMIF_BIT 0x0002 648c2ecf20Sopenharmony_ci#define I2CMAL_BIT 0x0010 658c2ecf20Sopenharmony_ci#define I2CBMFI_BIT 0x0001 668c2ecf20Sopenharmony_ci#define I2CBMAL_BIT 0x0002 678c2ecf20Sopenharmony_ci#define I2CBMNA_BIT 0x0004 688c2ecf20Sopenharmony_ci#define I2CBMTO_BIT 0x0008 698c2ecf20Sopenharmony_ci#define I2CBMIS_BIT 0x0010 708c2ecf20Sopenharmony_ci#define I2CESRFI_BIT 0X0001 718c2ecf20Sopenharmony_ci#define I2CESRTO_BIT 0x0002 728c2ecf20Sopenharmony_ci#define I2CESRFIIE_BIT 0x1 738c2ecf20Sopenharmony_ci#define I2CESRTOIE_BIT 0x2 748c2ecf20Sopenharmony_ci#define I2CBMDZ_BIT 0x0040 758c2ecf20Sopenharmony_ci#define I2CBMAG_BIT 0x0020 768c2ecf20Sopenharmony_ci#define I2CMBB_BIT 0x0020 778c2ecf20Sopenharmony_ci#define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \ 788c2ecf20Sopenharmony_ci I2CBMTO_BIT | I2CBMIS_BIT) 798c2ecf20Sopenharmony_ci#define I2C_ADDR_MSK 0xFF 808c2ecf20Sopenharmony_ci#define I2C_MSB_2B_MSK 0x300 818c2ecf20Sopenharmony_ci#define FAST_MODE_CLK 400 828c2ecf20Sopenharmony_ci#define FAST_MODE_EN 0x0001 838c2ecf20Sopenharmony_ci#define SUB_ADDR_LEN_MAX 4 848c2ecf20Sopenharmony_ci#define BUF_LEN_MAX 32 858c2ecf20Sopenharmony_ci#define PCH_BUFFER_MODE 0x1 868c2ecf20Sopenharmony_ci#define EEPROM_SW_RST_MODE 0x0002 878c2ecf20Sopenharmony_ci#define NORMAL_INTR_ENBL 0x0300 888c2ecf20Sopenharmony_ci#define EEPROM_RST_INTR_ENBL (I2CESRFIIE_BIT | I2CESRTOIE_BIT) 898c2ecf20Sopenharmony_ci#define EEPROM_RST_INTR_DISBL 0x0 908c2ecf20Sopenharmony_ci#define BUFFER_MODE_INTR_ENBL 0x001F 918c2ecf20Sopenharmony_ci#define BUFFER_MODE_INTR_DISBL 0x0 928c2ecf20Sopenharmony_ci#define NORMAL_MODE 0x0 938c2ecf20Sopenharmony_ci#define BUFFER_MODE 0x1 948c2ecf20Sopenharmony_ci#define EEPROM_SR_MODE 0x2 958c2ecf20Sopenharmony_ci#define I2C_TX_MODE 0x0010 968c2ecf20Sopenharmony_ci#define PCH_BUF_TX 0xFFF7 978c2ecf20Sopenharmony_ci#define PCH_BUF_RD 0x0008 988c2ecf20Sopenharmony_ci#define I2C_ERROR_MASK (I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \ 998c2ecf20Sopenharmony_ci I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT) 1008c2ecf20Sopenharmony_ci#define I2CMAL_EVENT 0x0001 1018c2ecf20Sopenharmony_ci#define I2CMCF_EVENT 0x0002 1028c2ecf20Sopenharmony_ci#define I2CBMFI_EVENT 0x0004 1038c2ecf20Sopenharmony_ci#define I2CBMAL_EVENT 0x0008 1048c2ecf20Sopenharmony_ci#define I2CBMNA_EVENT 0x0010 1058c2ecf20Sopenharmony_ci#define I2CBMTO_EVENT 0x0020 1068c2ecf20Sopenharmony_ci#define I2CBMIS_EVENT 0x0040 1078c2ecf20Sopenharmony_ci#define I2CESRFI_EVENT 0x0080 1088c2ecf20Sopenharmony_ci#define I2CESRTO_EVENT 0x0100 1098c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_PCH_I2C 0x8817 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define pch_dbg(adap, fmt, arg...) \ 1128c2ecf20Sopenharmony_ci dev_dbg(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define pch_err(adap, fmt, arg...) \ 1158c2ecf20Sopenharmony_ci dev_err(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define pch_pci_err(pdev, fmt, arg...) \ 1188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s :" fmt, __func__, ##arg) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define pch_pci_dbg(pdev, fmt, arg...) \ 1218c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ciSet the number of I2C instance max 1258c2ecf20Sopenharmony_ciIntel EG20T PCH : 1ch 1268c2ecf20Sopenharmony_ciLAPIS Semiconductor ML7213 IOH : 2ch 1278c2ecf20Sopenharmony_ciLAPIS Semiconductor ML7831 IOH : 1ch 1288c2ecf20Sopenharmony_ci*/ 1298c2ecf20Sopenharmony_ci#define PCH_I2C_MAX_DEV 2 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * struct i2c_algo_pch_data - for I2C driver functionalities 1338c2ecf20Sopenharmony_ci * @pch_adapter: stores the reference to i2c_adapter structure 1348c2ecf20Sopenharmony_ci * @p_adapter_info: stores the reference to adapter_info structure 1358c2ecf20Sopenharmony_ci * @pch_base_address: specifies the remapped base address 1368c2ecf20Sopenharmony_ci * @pch_buff_mode_en: specifies if buffer mode is enabled 1378c2ecf20Sopenharmony_ci * @pch_event_flag: specifies occurrence of interrupt events 1388c2ecf20Sopenharmony_ci * @pch_i2c_xfer_in_progress: specifies whether the transfer is completed 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistruct i2c_algo_pch_data { 1418c2ecf20Sopenharmony_ci struct i2c_adapter pch_adapter; 1428c2ecf20Sopenharmony_ci struct adapter_info *p_adapter_info; 1438c2ecf20Sopenharmony_ci void __iomem *pch_base_address; 1448c2ecf20Sopenharmony_ci int pch_buff_mode_en; 1458c2ecf20Sopenharmony_ci u32 pch_event_flag; 1468c2ecf20Sopenharmony_ci bool pch_i2c_xfer_in_progress; 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * struct adapter_info - This structure holds the adapter information for the 1518c2ecf20Sopenharmony_ci PCH i2c controller 1528c2ecf20Sopenharmony_ci * @pch_data: stores a list of i2c_algo_pch_data 1538c2ecf20Sopenharmony_ci * @pch_i2c_suspended: specifies whether the system is suspended or not 1548c2ecf20Sopenharmony_ci * perhaps with more lines and words. 1558c2ecf20Sopenharmony_ci * @ch_num: specifies the number of i2c instance 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * pch_data has as many elements as maximum I2C channels 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistruct adapter_info { 1608c2ecf20Sopenharmony_ci struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV]; 1618c2ecf20Sopenharmony_ci bool pch_i2c_suspended; 1628c2ecf20Sopenharmony_ci int ch_num; 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int pch_i2c_speed = 100; /* I2C bus speed in Kbps */ 1678c2ecf20Sopenharmony_cistatic int pch_clk = 50000; /* specifies I2C clock speed in KHz */ 1688c2ecf20Sopenharmony_cistatic wait_queue_head_t pch_event; 1698c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pch_mutex); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Definition for ML7213 by LAPIS Semiconductor */ 1728c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_ML7213_I2C 0x802D 1738c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_ML7223_I2C 0x8010 1748c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_ML7831_I2C 0x8817 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct pci_device_id pch_pcidev_id[] = { 1778c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, 1788c2ecf20Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, 1798c2ecf20Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, 1808c2ecf20Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, }, 1818c2ecf20Sopenharmony_ci {0,} 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pch_pcidev_id); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic irqreturn_t pch_i2c_handler(int irq, void *pData); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u32 val; 1908c2ecf20Sopenharmony_ci val = ioread32(addr + offset); 1918c2ecf20Sopenharmony_ci val |= bitmask; 1928c2ecf20Sopenharmony_ci iowrite32(val, addr + offset); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci u32 val; 1988c2ecf20Sopenharmony_ci val = ioread32(addr + offset); 1998c2ecf20Sopenharmony_ci val &= (~bitmask); 2008c2ecf20Sopenharmony_ci iowrite32(val, addr + offset); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/** 2048c2ecf20Sopenharmony_ci * pch_i2c_init() - hardware initialization of I2C module 2058c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic void pch_i2c_init(struct i2c_algo_pch_data *adap) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 2108c2ecf20Sopenharmony_ci u32 pch_i2cbc; 2118c2ecf20Sopenharmony_ci u32 pch_i2ctmr; 2128c2ecf20Sopenharmony_ci u32 reg_value; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* reset I2C controller */ 2158c2ecf20Sopenharmony_ci iowrite32(0x01, p + PCH_I2CSRST); 2168c2ecf20Sopenharmony_ci msleep(20); 2178c2ecf20Sopenharmony_ci iowrite32(0x0, p + PCH_I2CSRST); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Initialize I2C registers */ 2208c2ecf20Sopenharmony_ci iowrite32(0x21, p + PCH_I2CNF); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (pch_i2c_speed != 400) 2258c2ecf20Sopenharmony_ci pch_i2c_speed = 100; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg_value = PCH_I2CCTL_I2CMEN; 2288c2ecf20Sopenharmony_ci if (pch_i2c_speed == FAST_MODE_CLK) { 2298c2ecf20Sopenharmony_ci reg_value |= FAST_MODE_EN; 2308c2ecf20Sopenharmony_ci pch_dbg(adap, "Fast mode enabled\n"); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (pch_clk > PCH_MAX_CLK) 2348c2ecf20Sopenharmony_ci pch_clk = 62500; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); 2378c2ecf20Sopenharmony_ci /* Set transfer speed in I2CBC */ 2388c2ecf20Sopenharmony_ci iowrite32(pch_i2cbc, p + PCH_I2CBC); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci pch_i2ctmr = (pch_clk) / 8; 2418c2ecf20Sopenharmony_ci iowrite32(pch_i2ctmr, p + PCH_I2CTMR); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci reg_value |= NORMAL_INTR_ENBL; /* Enable interrupts in normal mode */ 2448c2ecf20Sopenharmony_ci iowrite32(reg_value, p + PCH_I2CCTL); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci pch_dbg(adap, 2478c2ecf20Sopenharmony_ci "I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n", 2488c2ecf20Sopenharmony_ci ioread32(p + PCH_I2CCTL), pch_i2cbc, pch_i2ctmr); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci init_waitqueue_head(&pch_event); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * pch_i2c_wait_for_bus_idle() - check the status of bus. 2558c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 2568c2ecf20Sopenharmony_ci * @timeout: waiting time counter (ms). 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cistatic s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, 2598c2ecf20Sopenharmony_ci s32 timeout) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 2628c2ecf20Sopenharmony_ci int schedule = 0; 2638c2ecf20Sopenharmony_ci unsigned long end = jiffies + msecs_to_jiffies(timeout); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) { 2668c2ecf20Sopenharmony_ci if (time_after(jiffies, end)) { 2678c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); 2688c2ecf20Sopenharmony_ci pch_err(adap, "%s: Timeout Error.return%d\n", 2698c2ecf20Sopenharmony_ci __func__, -ETIME); 2708c2ecf20Sopenharmony_ci pch_i2c_init(adap); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return -ETIME; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!schedule) 2768c2ecf20Sopenharmony_ci /* Retry after some usecs */ 2778c2ecf20Sopenharmony_ci udelay(5); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci /* Wait a bit more without consuming CPU */ 2808c2ecf20Sopenharmony_ci usleep_range(20, 1000); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci schedule = 1; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * pch_i2c_start() - Generate I2C start condition in normal mode. 2908c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic void pch_i2c_start(struct i2c_algo_pch_data *adap) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 2978c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 2988c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/** 3028c2ecf20Sopenharmony_ci * pch_i2c_stop() - generate stop condition in normal mode. 3038c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic void pch_i2c_stop(struct i2c_algo_pch_data *adap) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 3088c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 3098c2ecf20Sopenharmony_ci /* clear the start bit */ 3108c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int pch_i2c_wait_for_check_xfer(struct i2c_algo_pch_data *adap) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci long ret; 3168c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = wait_event_timeout(pch_event, 3198c2ecf20Sopenharmony_ci (adap->pch_event_flag != 0), msecs_to_jiffies(1000)); 3208c2ecf20Sopenharmony_ci if (!ret) { 3218c2ecf20Sopenharmony_ci pch_err(adap, "%s:wait-event timeout\n", __func__); 3228c2ecf20Sopenharmony_ci adap->pch_event_flag = 0; 3238c2ecf20Sopenharmony_ci pch_i2c_stop(adap); 3248c2ecf20Sopenharmony_ci pch_i2c_init(adap); 3258c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (adap->pch_event_flag & I2C_ERROR_MASK) { 3298c2ecf20Sopenharmony_ci pch_err(adap, "Lost Arbitration\n"); 3308c2ecf20Sopenharmony_ci adap->pch_event_flag = 0; 3318c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT); 3328c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); 3338c2ecf20Sopenharmony_ci pch_i2c_init(adap); 3348c2ecf20Sopenharmony_ci return -EAGAIN; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci adap->pch_event_flag = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (ioread32(p + PCH_I2CSR) & PCH_GETACK) { 3408c2ecf20Sopenharmony_ci pch_dbg(adap, "Receive NACK for slave address setting\n"); 3418c2ecf20Sopenharmony_ci return -ENXIO; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/** 3488c2ecf20Sopenharmony_ci * pch_i2c_repstart() - generate repeated start condition in normal mode 3498c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void pch_i2c_repstart(struct i2c_algo_pch_data *adap) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 3548c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 3558c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_REPSTART); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/** 3598c2ecf20Sopenharmony_ci * pch_i2c_writebytes() - write data to I2C bus in normal mode 3608c2ecf20Sopenharmony_ci * @i2c_adap: Pointer to the struct i2c_adapter. 3618c2ecf20Sopenharmony_ci * @last: specifies whether last message or not. 3628c2ecf20Sopenharmony_ci * In the case of compound mode it will be 1 for last message, 3638c2ecf20Sopenharmony_ci * otherwise 0. 3648c2ecf20Sopenharmony_ci * @first: specifies whether first message or not. 3658c2ecf20Sopenharmony_ci * 1 for first message otherwise 0. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, 3688c2ecf20Sopenharmony_ci struct i2c_msg *msgs, u32 last, u32 first) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct i2c_algo_pch_data *adap = i2c_adap->algo_data; 3718c2ecf20Sopenharmony_ci u8 *buf; 3728c2ecf20Sopenharmony_ci u32 length; 3738c2ecf20Sopenharmony_ci u32 addr; 3748c2ecf20Sopenharmony_ci u32 addr_2_msb; 3758c2ecf20Sopenharmony_ci u32 addr_8_lsb; 3768c2ecf20Sopenharmony_ci s32 wrcount; 3778c2ecf20Sopenharmony_ci s32 rtn; 3788c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci length = msgs->len; 3818c2ecf20Sopenharmony_ci buf = msgs->buf; 3828c2ecf20Sopenharmony_ci addr = msgs->addr; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* enable master tx */ 3858c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x msgs->len = %d\n", ioread32(p + PCH_I2CCTL), 3888c2ecf20Sopenharmony_ci length); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (first) { 3918c2ecf20Sopenharmony_ci if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) 3928c2ecf20Sopenharmony_ci return -ETIME; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (msgs->flags & I2C_M_TEN) { 3968c2ecf20Sopenharmony_ci addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06; 3978c2ecf20Sopenharmony_ci iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); 3988c2ecf20Sopenharmony_ci if (first) 3998c2ecf20Sopenharmony_ci pch_i2c_start(adap); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 4028c2ecf20Sopenharmony_ci if (rtn) 4038c2ecf20Sopenharmony_ci return rtn; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci addr_8_lsb = (addr & I2C_ADDR_MSK); 4068c2ecf20Sopenharmony_ci iowrite32(addr_8_lsb, p + PCH_I2CDR); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci /* set 7 bit slave address and R/W bit as 0 */ 4098c2ecf20Sopenharmony_ci iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR); 4108c2ecf20Sopenharmony_ci if (first) 4118c2ecf20Sopenharmony_ci pch_i2c_start(adap); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 4158c2ecf20Sopenharmony_ci if (rtn) 4168c2ecf20Sopenharmony_ci return rtn; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci for (wrcount = 0; wrcount < length; ++wrcount) { 4198c2ecf20Sopenharmony_ci /* write buffer value to I2C data register */ 4208c2ecf20Sopenharmony_ci iowrite32(buf[wrcount], p + PCH_I2CDR); 4218c2ecf20Sopenharmony_ci pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 4248c2ecf20Sopenharmony_ci if (rtn) 4258c2ecf20Sopenharmony_ci return rtn; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMCF_BIT); 4288c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* check if this is the last message */ 4328c2ecf20Sopenharmony_ci if (last) 4338c2ecf20Sopenharmony_ci pch_i2c_stop(adap); 4348c2ecf20Sopenharmony_ci else 4358c2ecf20Sopenharmony_ci pch_i2c_repstart(adap); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci pch_dbg(adap, "return=%d\n", wrcount); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return wrcount; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci/** 4438c2ecf20Sopenharmony_ci * pch_i2c_sendack() - send ACK 4448c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_cistatic void pch_i2c_sendack(struct i2c_algo_pch_data *adap) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 4498c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 4508c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/** 4548c2ecf20Sopenharmony_ci * pch_i2c_sendnack() - send NACK 4558c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_cistatic void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 4608c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 4618c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci/** 4658c2ecf20Sopenharmony_ci * pch_i2c_restart() - Generate I2C restart condition in normal mode. 4668c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 4678c2ecf20Sopenharmony_ci * 4688c2ecf20Sopenharmony_ci * Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_cistatic void pch_i2c_restart(struct i2c_algo_pch_data *adap) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 4738c2ecf20Sopenharmony_ci pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); 4748c2ecf20Sopenharmony_ci pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/** 4788c2ecf20Sopenharmony_ci * pch_i2c_readbytes() - read data from I2C bus in normal mode. 4798c2ecf20Sopenharmony_ci * @i2c_adap: Pointer to the struct i2c_adapter. 4808c2ecf20Sopenharmony_ci * @msgs: Pointer to i2c_msg structure. 4818c2ecf20Sopenharmony_ci * @last: specifies whether last message or not. 4828c2ecf20Sopenharmony_ci * @first: specifies whether first message or not. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, 4858c2ecf20Sopenharmony_ci u32 last, u32 first) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct i2c_algo_pch_data *adap = i2c_adap->algo_data; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci u8 *buf; 4908c2ecf20Sopenharmony_ci u32 count; 4918c2ecf20Sopenharmony_ci u32 length; 4928c2ecf20Sopenharmony_ci u32 addr; 4938c2ecf20Sopenharmony_ci u32 addr_2_msb; 4948c2ecf20Sopenharmony_ci u32 addr_8_lsb; 4958c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 4968c2ecf20Sopenharmony_ci s32 rtn; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci length = msgs->len; 4998c2ecf20Sopenharmony_ci buf = msgs->buf; 5008c2ecf20Sopenharmony_ci addr = msgs->addr; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* enable master reception */ 5038c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (first) { 5068c2ecf20Sopenharmony_ci if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) 5078c2ecf20Sopenharmony_ci return -ETIME; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (msgs->flags & I2C_M_TEN) { 5118c2ecf20Sopenharmony_ci addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); 5128c2ecf20Sopenharmony_ci iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); 5138c2ecf20Sopenharmony_ci if (first) 5148c2ecf20Sopenharmony_ci pch_i2c_start(adap); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 5178c2ecf20Sopenharmony_ci if (rtn) 5188c2ecf20Sopenharmony_ci return rtn; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci addr_8_lsb = (addr & I2C_ADDR_MSK); 5218c2ecf20Sopenharmony_ci iowrite32(addr_8_lsb, p + PCH_I2CDR); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci pch_i2c_restart(adap); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 5268c2ecf20Sopenharmony_ci if (rtn) 5278c2ecf20Sopenharmony_ci return rtn; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci addr_2_msb |= I2C_RD; 5308c2ecf20Sopenharmony_ci iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); 5318c2ecf20Sopenharmony_ci } else { 5328c2ecf20Sopenharmony_ci /* 7 address bits + R/W bit */ 5338c2ecf20Sopenharmony_ci iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* check if it is the first message */ 5378c2ecf20Sopenharmony_ci if (first) 5388c2ecf20Sopenharmony_ci pch_i2c_start(adap); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 5418c2ecf20Sopenharmony_ci if (rtn) 5428c2ecf20Sopenharmony_ci return rtn; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (length == 0) { 5458c2ecf20Sopenharmony_ci pch_i2c_stop(adap); 5468c2ecf20Sopenharmony_ci ioread32(p + PCH_I2CDR); /* Dummy read needs */ 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci count = length; 5498c2ecf20Sopenharmony_ci } else { 5508c2ecf20Sopenharmony_ci int read_index; 5518c2ecf20Sopenharmony_ci int loop; 5528c2ecf20Sopenharmony_ci pch_i2c_sendack(adap); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Dummy read */ 5558c2ecf20Sopenharmony_ci for (loop = 1, read_index = 0; loop < length; loop++) { 5568c2ecf20Sopenharmony_ci buf[read_index] = ioread32(p + PCH_I2CDR); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (loop != 1) 5598c2ecf20Sopenharmony_ci read_index++; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 5628c2ecf20Sopenharmony_ci if (rtn) 5638c2ecf20Sopenharmony_ci return rtn; 5648c2ecf20Sopenharmony_ci } /* end for */ 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci pch_i2c_sendnack(adap); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */ 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (length != 1) 5718c2ecf20Sopenharmony_ci read_index++; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci rtn = pch_i2c_wait_for_check_xfer(adap); 5748c2ecf20Sopenharmony_ci if (rtn) 5758c2ecf20Sopenharmony_ci return rtn; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (last) 5788c2ecf20Sopenharmony_ci pch_i2c_stop(adap); 5798c2ecf20Sopenharmony_ci else 5808c2ecf20Sopenharmony_ci pch_i2c_repstart(adap); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */ 5838c2ecf20Sopenharmony_ci count = read_index; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return count; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/** 5908c2ecf20Sopenharmony_ci * pch_i2c_cb() - Interrupt handler Call back function 5918c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic void pch_i2c_cb(struct i2c_algo_pch_data *adap) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci u32 sts; 5968c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci sts = ioread32(p + PCH_I2CSR); 5998c2ecf20Sopenharmony_ci sts &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT); 6008c2ecf20Sopenharmony_ci if (sts & I2CMAL_BIT) 6018c2ecf20Sopenharmony_ci adap->pch_event_flag |= I2CMAL_EVENT; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (sts & I2CMCF_BIT) 6048c2ecf20Sopenharmony_ci adap->pch_event_flag |= I2CMCF_EVENT; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* clear the applicable bits */ 6078c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CSR, sts); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci pch_dbg(adap, "PCH_I2CSR = %x\n", ioread32(p + PCH_I2CSR)); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci wake_up(&pch_event); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/** 6158c2ecf20Sopenharmony_ci * pch_i2c_handler() - interrupt handler for the PCH I2C controller 6168c2ecf20Sopenharmony_ci * @irq: irq number. 6178c2ecf20Sopenharmony_ci * @pData: cookie passed back to the handler function. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_cistatic irqreturn_t pch_i2c_handler(int irq, void *pData) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci u32 reg_val; 6228c2ecf20Sopenharmony_ci int flag; 6238c2ecf20Sopenharmony_ci int i; 6248c2ecf20Sopenharmony_ci struct adapter_info *adap_info = pData; 6258c2ecf20Sopenharmony_ci void __iomem *p; 6268c2ecf20Sopenharmony_ci u32 mode; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci for (i = 0, flag = 0; i < adap_info->ch_num; i++) { 6298c2ecf20Sopenharmony_ci p = adap_info->pch_data[i].pch_base_address; 6308c2ecf20Sopenharmony_ci mode = ioread32(p + PCH_I2CMOD); 6318c2ecf20Sopenharmony_ci mode &= BUFFER_MODE | EEPROM_SR_MODE; 6328c2ecf20Sopenharmony_ci if (mode != NORMAL_MODE) { 6338c2ecf20Sopenharmony_ci pch_err(adap_info->pch_data, 6348c2ecf20Sopenharmony_ci "I2C-%d mode(%d) is not supported\n", mode, i); 6358c2ecf20Sopenharmony_ci continue; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci reg_val = ioread32(p + PCH_I2CSR); 6388c2ecf20Sopenharmony_ci if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) { 6398c2ecf20Sopenharmony_ci pch_i2c_cb(&adap_info->pch_data[i]); 6408c2ecf20Sopenharmony_ci flag = 1; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return flag ? IRQ_HANDLED : IRQ_NONE; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/** 6488c2ecf20Sopenharmony_ci * pch_i2c_xfer() - Reading adnd writing data through I2C bus 6498c2ecf20Sopenharmony_ci * @i2c_adap: Pointer to the struct i2c_adapter. 6508c2ecf20Sopenharmony_ci * @msgs: Pointer to i2c_msg structure. 6518c2ecf20Sopenharmony_ci * @num: number of messages. 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_cistatic s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, 6548c2ecf20Sopenharmony_ci struct i2c_msg *msgs, s32 num) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 6578c2ecf20Sopenharmony_ci u32 i = 0; 6588c2ecf20Sopenharmony_ci u32 status; 6598c2ecf20Sopenharmony_ci s32 ret; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci struct i2c_algo_pch_data *adap = i2c_adap->algo_data; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pch_mutex); 6648c2ecf20Sopenharmony_ci if (ret) 6658c2ecf20Sopenharmony_ci return ret; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (adap->p_adapter_info->pch_i2c_suspended) { 6688c2ecf20Sopenharmony_ci mutex_unlock(&pch_mutex); 6698c2ecf20Sopenharmony_ci return -EBUSY; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci pch_dbg(adap, "adap->p_adapter_info->pch_i2c_suspended is %d\n", 6738c2ecf20Sopenharmony_ci adap->p_adapter_info->pch_i2c_suspended); 6748c2ecf20Sopenharmony_ci /* transfer not completed */ 6758c2ecf20Sopenharmony_ci adap->pch_i2c_xfer_in_progress = true; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci for (i = 0; i < num && ret >= 0; i++) { 6788c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 6798c2ecf20Sopenharmony_ci pmsg->flags |= adap->pch_buff_mode_en; 6808c2ecf20Sopenharmony_ci status = pmsg->flags; 6818c2ecf20Sopenharmony_ci pch_dbg(adap, 6828c2ecf20Sopenharmony_ci "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if ((status & (I2C_M_RD)) != false) { 6858c2ecf20Sopenharmony_ci ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), 6868c2ecf20Sopenharmony_ci (i == 0)); 6878c2ecf20Sopenharmony_ci } else { 6888c2ecf20Sopenharmony_ci ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num), 6898c2ecf20Sopenharmony_ci (i == 0)); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci adap->pch_i2c_xfer_in_progress = false; /* transfer completed */ 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci mutex_unlock(&pch_mutex); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return (ret < 0) ? ret : num; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/** 7018c2ecf20Sopenharmony_ci * pch_i2c_func() - return the functionality of the I2C driver 7028c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_cistatic u32 pch_i2c_func(struct i2c_adapter *adap) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic const struct i2c_algorithm pch_algorithm = { 7108c2ecf20Sopenharmony_ci .master_xfer = pch_i2c_xfer, 7118c2ecf20Sopenharmony_ci .functionality = pch_i2c_func 7128c2ecf20Sopenharmony_ci}; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/** 7158c2ecf20Sopenharmony_ci * pch_i2c_disbl_int() - Disable PCH I2C interrupts 7168c2ecf20Sopenharmony_ci * @adap: Pointer to struct i2c_algo_pch_data. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_cistatic void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci void __iomem *p = adap->pch_base_address; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci pch_clrbit(adap->pch_base_address, PCH_I2CCTL, NORMAL_INTR_ENBL); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int pch_i2c_probe(struct pci_dev *pdev, 7308c2ecf20Sopenharmony_ci const struct pci_device_id *id) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci void __iomem *base_addr; 7338c2ecf20Sopenharmony_ci int ret; 7348c2ecf20Sopenharmony_ci int i, j; 7358c2ecf20Sopenharmony_ci struct adapter_info *adap_info; 7368c2ecf20Sopenharmony_ci struct i2c_adapter *pch_adap; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci pch_pci_dbg(pdev, "Entered.\n"); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci adap_info = kzalloc((sizeof(struct adapter_info)), GFP_KERNEL); 7418c2ecf20Sopenharmony_ci if (adap_info == NULL) 7428c2ecf20Sopenharmony_ci return -ENOMEM; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 7458c2ecf20Sopenharmony_ci if (ret) { 7468c2ecf20Sopenharmony_ci pch_pci_err(pdev, "pci_enable_device FAILED\n"); 7478c2ecf20Sopenharmony_ci goto err_pci_enable; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci ret = pci_request_regions(pdev, KBUILD_MODNAME); 7518c2ecf20Sopenharmony_ci if (ret) { 7528c2ecf20Sopenharmony_ci pch_pci_err(pdev, "pci_request_regions FAILED\n"); 7538c2ecf20Sopenharmony_ci goto err_pci_req; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci base_addr = pci_iomap(pdev, 1, 0); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (base_addr == NULL) { 7598c2ecf20Sopenharmony_ci pch_pci_err(pdev, "pci_iomap FAILED\n"); 7608c2ecf20Sopenharmony_ci ret = -ENOMEM; 7618c2ecf20Sopenharmony_ci goto err_pci_iomap; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Set the number of I2C channel instance */ 7658c2ecf20Sopenharmony_ci adap_info->ch_num = id->driver_data; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) { 7688c2ecf20Sopenharmony_ci pch_adap = &adap_info->pch_data[i].pch_adapter; 7698c2ecf20Sopenharmony_ci adap_info->pch_i2c_suspended = false; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci adap_info->pch_data[i].p_adapter_info = adap_info; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci pch_adap->owner = THIS_MODULE; 7748c2ecf20Sopenharmony_ci pch_adap->class = I2C_CLASS_HWMON; 7758c2ecf20Sopenharmony_ci strlcpy(pch_adap->name, KBUILD_MODNAME, sizeof(pch_adap->name)); 7768c2ecf20Sopenharmony_ci pch_adap->algo = &pch_algorithm; 7778c2ecf20Sopenharmony_ci pch_adap->algo_data = &adap_info->pch_data[i]; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* base_addr + offset; */ 7808c2ecf20Sopenharmony_ci adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci pch_adap->dev.of_node = pdev->dev.of_node; 7838c2ecf20Sopenharmony_ci pch_adap->dev.parent = &pdev->dev; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, 7878c2ecf20Sopenharmony_ci KBUILD_MODNAME, adap_info); 7888c2ecf20Sopenharmony_ci if (ret) { 7898c2ecf20Sopenharmony_ci pch_pci_err(pdev, "request_irq FAILED\n"); 7908c2ecf20Sopenharmony_ci goto err_request_irq; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) { 7948c2ecf20Sopenharmony_ci pch_adap = &adap_info->pch_data[i].pch_adapter; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci pch_i2c_init(&adap_info->pch_data[i]); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci pch_adap->nr = i; 7998c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(pch_adap); 8008c2ecf20Sopenharmony_ci if (ret) { 8018c2ecf20Sopenharmony_ci pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); 8028c2ecf20Sopenharmony_ci goto err_add_adapter; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, adap_info); 8078c2ecf20Sopenharmony_ci pch_pci_dbg(pdev, "returns %d.\n", ret); 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cierr_add_adapter: 8118c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 8128c2ecf20Sopenharmony_ci i2c_del_adapter(&adap_info->pch_data[j].pch_adapter); 8138c2ecf20Sopenharmony_ci free_irq(pdev->irq, adap_info); 8148c2ecf20Sopenharmony_cierr_request_irq: 8158c2ecf20Sopenharmony_ci pci_iounmap(pdev, base_addr); 8168c2ecf20Sopenharmony_cierr_pci_iomap: 8178c2ecf20Sopenharmony_ci pci_release_regions(pdev); 8188c2ecf20Sopenharmony_cierr_pci_req: 8198c2ecf20Sopenharmony_ci pci_disable_device(pdev); 8208c2ecf20Sopenharmony_cierr_pci_enable: 8218c2ecf20Sopenharmony_ci kfree(adap_info); 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void pch_i2c_remove(struct pci_dev *pdev) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci int i; 8288c2ecf20Sopenharmony_ci struct adapter_info *adap_info = pci_get_drvdata(pdev); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci free_irq(pdev->irq, adap_info); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) { 8338c2ecf20Sopenharmony_ci pch_i2c_disbl_int(&adap_info->pch_data[i]); 8348c2ecf20Sopenharmony_ci i2c_del_adapter(&adap_info->pch_data[i].pch_adapter); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (adap_info->pch_data[0].pch_base_address) 8388c2ecf20Sopenharmony_ci pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) 8418c2ecf20Sopenharmony_ci adap_info->pch_data[i].pch_base_address = NULL; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci pci_release_regions(pdev); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci pci_disable_device(pdev); 8468c2ecf20Sopenharmony_ci kfree(adap_info); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int __maybe_unused pch_i2c_suspend(struct device *dev) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci int i; 8528c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 8538c2ecf20Sopenharmony_ci struct adapter_info *adap_info = pci_get_drvdata(pdev); 8548c2ecf20Sopenharmony_ci void __iomem *p = adap_info->pch_data[0].pch_base_address; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci adap_info->pch_i2c_suspended = true; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) { 8598c2ecf20Sopenharmony_ci while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) { 8608c2ecf20Sopenharmony_ci /* Wait until all channel transfers are completed */ 8618c2ecf20Sopenharmony_ci msleep(20); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci /* Disable the i2c interrupts */ 8668c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) 8678c2ecf20Sopenharmony_ci pch_i2c_disbl_int(&adap_info->pch_data[i]); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x " 8708c2ecf20Sopenharmony_ci "invoked function pch_i2c_disbl_int successfully\n", 8718c2ecf20Sopenharmony_ci ioread32(p + PCH_I2CSR), ioread32(p + PCH_I2CBUFSTA), 8728c2ecf20Sopenharmony_ci ioread32(p + PCH_I2CESRSTA)); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int __maybe_unused pch_i2c_resume(struct device *dev) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci int i; 8808c2ecf20Sopenharmony_ci struct adapter_info *adap_info = dev_get_drvdata(dev); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci for (i = 0; i < adap_info->ch_num; i++) 8838c2ecf20Sopenharmony_ci pch_i2c_init(&adap_info->pch_data[i]); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci adap_info->pch_i2c_suspended = false; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pch_i2c_pm_ops, pch_i2c_suspend, pch_i2c_resume); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic struct pci_driver pch_pcidriver = { 8938c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 8948c2ecf20Sopenharmony_ci .id_table = pch_pcidev_id, 8958c2ecf20Sopenharmony_ci .probe = pch_i2c_probe, 8968c2ecf20Sopenharmony_ci .remove = pch_i2c_remove, 8978c2ecf20Sopenharmony_ci .driver.pm = &pch_i2c_pm_ops, 8988c2ecf20Sopenharmony_ci}; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cimodule_pci_driver(pch_pcidriver); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); 9038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tomoya MORINAGA. <tomoya.rohm@gmail.com>"); 9058c2ecf20Sopenharmony_cimodule_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); 9068c2ecf20Sopenharmony_cimodule_param(pch_clk, int, (S_IRUSR | S_IWUSR)); 907