18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 PA Semi, Inc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * SMBus host driver for PA Semi PWRficient 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/stddef.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic struct pci_driver pasemi_smb_driver; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct pasemi_smbus { 218c2ecf20Sopenharmony_ci struct pci_dev *dev; 228c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 238c2ecf20Sopenharmony_ci unsigned long base; 248c2ecf20Sopenharmony_ci int size; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Register offsets */ 288c2ecf20Sopenharmony_ci#define REG_MTXFIFO 0x00 298c2ecf20Sopenharmony_ci#define REG_MRXFIFO 0x04 308c2ecf20Sopenharmony_ci#define REG_SMSTA 0x14 318c2ecf20Sopenharmony_ci#define REG_CTL 0x1c 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Register defs */ 348c2ecf20Sopenharmony_ci#define MTXFIFO_READ 0x00000400 358c2ecf20Sopenharmony_ci#define MTXFIFO_STOP 0x00000200 368c2ecf20Sopenharmony_ci#define MTXFIFO_START 0x00000100 378c2ecf20Sopenharmony_ci#define MTXFIFO_DATA_M 0x000000ff 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MRXFIFO_EMPTY 0x00000100 408c2ecf20Sopenharmony_ci#define MRXFIFO_DATA_M 0x000000ff 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define SMSTA_XEN 0x08000000 438c2ecf20Sopenharmony_ci#define SMSTA_MTN 0x00200000 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define CTL_MRR 0x00000400 468c2ecf20Sopenharmony_ci#define CTL_MTR 0x00000200 478c2ecf20Sopenharmony_ci#define CTL_CLK_M 0x000000ff 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define CLK_100K_DIV 84 508c2ecf20Sopenharmony_ci#define CLK_400K_DIV 21 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline void reg_write(struct pasemi_smbus *smbus, int reg, int val) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n", 558c2ecf20Sopenharmony_ci smbus->base + reg, val); 568c2ecf20Sopenharmony_ci outl(val, smbus->base + reg); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline int reg_read(struct pasemi_smbus *smbus, int reg) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci ret = inl(smbus->base + reg); 638c2ecf20Sopenharmony_ci dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n", 648c2ecf20Sopenharmony_ci smbus->base + reg, ret); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define TXFIFO_WR(smbus, reg) reg_write((smbus), REG_MTXFIFO, (reg)) 698c2ecf20Sopenharmony_ci#define RXFIFO_RD(smbus) reg_read((smbus), REG_MRXFIFO) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void pasemi_smb_clear(struct pasemi_smbus *smbus) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned int status; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci status = reg_read(smbus, REG_SMSTA); 768c2ecf20Sopenharmony_ci reg_write(smbus, REG_SMSTA, status); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int pasemi_smb_waitready(struct pasemi_smbus *smbus) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int timeout = 10; 828c2ecf20Sopenharmony_ci unsigned int status; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci status = reg_read(smbus, REG_SMSTA); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (!(status & SMSTA_XEN) && timeout--) { 878c2ecf20Sopenharmony_ci msleep(1); 888c2ecf20Sopenharmony_ci status = reg_read(smbus, REG_SMSTA); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Got NACK? */ 928c2ecf20Sopenharmony_ci if (status & SMSTA_MTN) 938c2ecf20Sopenharmony_ci return -ENXIO; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (timeout < 0) { 968c2ecf20Sopenharmony_ci dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status); 978c2ecf20Sopenharmony_ci reg_write(smbus, REG_SMSTA, status); 988c2ecf20Sopenharmony_ci return -ETIME; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Clear XEN */ 1028c2ecf20Sopenharmony_ci reg_write(smbus, REG_SMSTA, SMSTA_XEN); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter, 1088c2ecf20Sopenharmony_ci struct i2c_msg *msg, int stop) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct pasemi_smbus *smbus = adapter->algo_data; 1118c2ecf20Sopenharmony_ci int read, i, err; 1128c2ecf20Sopenharmony_ci u32 rd; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci read = msg->flags & I2C_M_RD ? 1 : 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, MTXFIFO_START | i2c_8bit_addr_from_msg(msg)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (read) { 1198c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, msg->len | MTXFIFO_READ | 1208c2ecf20Sopenharmony_ci (stop ? MTXFIFO_STOP : 0)); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci err = pasemi_smb_waitready(smbus); 1238c2ecf20Sopenharmony_ci if (err) 1248c2ecf20Sopenharmony_ci goto reset_out; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = 0; i < msg->len; i++) { 1278c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 1288c2ecf20Sopenharmony_ci if (rd & MRXFIFO_EMPTY) { 1298c2ecf20Sopenharmony_ci err = -ENODATA; 1308c2ecf20Sopenharmony_ci goto reset_out; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci msg->buf[i] = rd & MRXFIFO_DATA_M; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci for (i = 0; i < msg->len - 1; i++) 1368c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, msg->buf[i]); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, msg->buf[msg->len-1] | 1398c2ecf20Sopenharmony_ci (stop ? MTXFIFO_STOP : 0)); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (stop) { 1428c2ecf20Sopenharmony_ci err = pasemi_smb_waitready(smbus); 1438c2ecf20Sopenharmony_ci if (err) 1448c2ecf20Sopenharmony_ci goto reset_out; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci reset_out: 1518c2ecf20Sopenharmony_ci reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 1528c2ecf20Sopenharmony_ci (CLK_100K_DIV & CTL_CLK_M))); 1538c2ecf20Sopenharmony_ci return err; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int pasemi_i2c_xfer(struct i2c_adapter *adapter, 1578c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct pasemi_smbus *smbus = adapter->algo_data; 1608c2ecf20Sopenharmony_ci int ret, i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci pasemi_smb_clear(smbus); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < num && !ret; i++) 1678c2ecf20Sopenharmony_ci ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1))); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return ret ? ret : num; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int pasemi_smb_xfer(struct i2c_adapter *adapter, 1738c2ecf20Sopenharmony_ci u16 addr, unsigned short flags, char read_write, u8 command, 1748c2ecf20Sopenharmony_ci int size, union i2c_smbus_data *data) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct pasemi_smbus *smbus = adapter->algo_data; 1778c2ecf20Sopenharmony_ci unsigned int rd; 1788c2ecf20Sopenharmony_ci int read_flag, err; 1798c2ecf20Sopenharmony_ci int len = 0, i; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* All our ops take 8-bit shifted addresses */ 1828c2ecf20Sopenharmony_ci addr <<= 1; 1838c2ecf20Sopenharmony_ci read_flag = read_write == I2C_SMBUS_READ; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pasemi_smb_clear(smbus); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci switch (size) { 1888c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 1898c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START | 1908c2ecf20Sopenharmony_ci MTXFIFO_STOP); 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 1938c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START); 1948c2ecf20Sopenharmony_ci if (read_write) 1958c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, MTXFIFO_STOP | command); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2008c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | MTXFIFO_START); 2018c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, command); 2028c2ecf20Sopenharmony_ci if (read_write) { 2038c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 2048c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP); 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 2108c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | MTXFIFO_START); 2118c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, command); 2128c2ecf20Sopenharmony_ci if (read_write) { 2138c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 2148c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP); 2158c2ecf20Sopenharmony_ci } else { 2168c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); 2178c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8)); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 2218c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | MTXFIFO_START); 2228c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, command); 2238c2ecf20Sopenharmony_ci if (read_write) { 2248c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 2258c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, 1 | MTXFIFO_READ); 2268c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 2278c2ecf20Sopenharmony_ci len = min_t(u8, (rd & MRXFIFO_DATA_M), 2288c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX); 2298c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, len | MTXFIFO_READ | 2308c2ecf20Sopenharmony_ci MTXFIFO_STOP); 2318c2ecf20Sopenharmony_ci } else { 2328c2ecf20Sopenharmony_ci len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX); 2338c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, len); 2348c2ecf20Sopenharmony_ci for (i = 1; i < len; i++) 2358c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, data->block[i]); 2368c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 2408c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_READ; 2418c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | MTXFIFO_START); 2428c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, command); 2438c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); 2448c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M); 2458c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 2468c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_PROC_CALL: 2498c2ecf20Sopenharmony_ci len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1); 2508c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_READ; 2518c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | MTXFIFO_START); 2528c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, command); 2538c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, len); 2548c2ecf20Sopenharmony_ci for (i = 1; i <= len; i++) 2558c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, data->block[i]); 2568c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, addr | I2C_SMBUS_READ); 2578c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, MTXFIFO_READ | 1); 2588c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 2598c2ecf20Sopenharmony_ci len = min_t(u8, (rd & MRXFIFO_DATA_M), 2608c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX - len); 2618c2ecf20Sopenharmony_ci TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci err = pasemi_smb_waitready(smbus); 2708c2ecf20Sopenharmony_ci if (err) 2718c2ecf20Sopenharmony_ci goto reset_out; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci switch (size) { 2778c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 2788c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2798c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 2808c2ecf20Sopenharmony_ci if (rd & MRXFIFO_EMPTY) { 2818c2ecf20Sopenharmony_ci err = -ENODATA; 2828c2ecf20Sopenharmony_ci goto reset_out; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci data->byte = rd & MRXFIFO_DATA_M; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 2878c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 2888c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 2898c2ecf20Sopenharmony_ci if (rd & MRXFIFO_EMPTY) { 2908c2ecf20Sopenharmony_ci err = -ENODATA; 2918c2ecf20Sopenharmony_ci goto reset_out; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci data->word = rd & MRXFIFO_DATA_M; 2948c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 2958c2ecf20Sopenharmony_ci if (rd & MRXFIFO_EMPTY) { 2968c2ecf20Sopenharmony_ci err = -ENODATA; 2978c2ecf20Sopenharmony_ci goto reset_out; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci data->word |= (rd & MRXFIFO_DATA_M) << 8; 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 3028c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_PROC_CALL: 3038c2ecf20Sopenharmony_ci data->block[0] = len; 3048c2ecf20Sopenharmony_ci for (i = 1; i <= len; i ++) { 3058c2ecf20Sopenharmony_ci rd = RXFIFO_RD(smbus); 3068c2ecf20Sopenharmony_ci if (rd & MRXFIFO_EMPTY) { 3078c2ecf20Sopenharmony_ci err = -ENODATA; 3088c2ecf20Sopenharmony_ci goto reset_out; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci data->block[i] = rd & MRXFIFO_DATA_M; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci reset_out: 3188c2ecf20Sopenharmony_ci reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 3198c2ecf20Sopenharmony_ci (CLK_100K_DIV & CTL_CLK_M))); 3208c2ecf20Sopenharmony_ci return err; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic u32 pasemi_smb_func(struct i2c_adapter *adapter) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 3268c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 3278c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | 3288c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 3328c2ecf20Sopenharmony_ci .master_xfer = pasemi_i2c_xfer, 3338c2ecf20Sopenharmony_ci .smbus_xfer = pasemi_smb_xfer, 3348c2ecf20Sopenharmony_ci .functionality = pasemi_smb_func, 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int pasemi_smb_probe(struct pci_dev *dev, 3388c2ecf20Sopenharmony_ci const struct pci_device_id *id) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct pasemi_smbus *smbus; 3418c2ecf20Sopenharmony_ci int error; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) 3448c2ecf20Sopenharmony_ci return -ENODEV; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (!smbus) 3488c2ecf20Sopenharmony_ci return -ENOMEM; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci smbus->dev = dev; 3518c2ecf20Sopenharmony_ci smbus->base = pci_resource_start(dev, 0); 3528c2ecf20Sopenharmony_ci smbus->size = pci_resource_len(dev, 0); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (!request_region(smbus->base, smbus->size, 3558c2ecf20Sopenharmony_ci pasemi_smb_driver.name)) { 3568c2ecf20Sopenharmony_ci error = -EBUSY; 3578c2ecf20Sopenharmony_ci goto out_kfree; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci smbus->adapter.owner = THIS_MODULE; 3618c2ecf20Sopenharmony_ci snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), 3628c2ecf20Sopenharmony_ci "PA Semi SMBus adapter at 0x%lx", smbus->base); 3638c2ecf20Sopenharmony_ci smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 3648c2ecf20Sopenharmony_ci smbus->adapter.algo = &smbus_algorithm; 3658c2ecf20Sopenharmony_ci smbus->adapter.algo_data = smbus; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 3688c2ecf20Sopenharmony_ci smbus->adapter.dev.parent = &dev->dev; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 3718c2ecf20Sopenharmony_ci (CLK_100K_DIV & CTL_CLK_M))); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci error = i2c_add_adapter(&smbus->adapter); 3748c2ecf20Sopenharmony_ci if (error) 3758c2ecf20Sopenharmony_ci goto out_release_region; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci pci_set_drvdata(dev, smbus); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci out_release_region: 3828c2ecf20Sopenharmony_ci release_region(smbus->base, smbus->size); 3838c2ecf20Sopenharmony_ci out_kfree: 3848c2ecf20Sopenharmony_ci kfree(smbus); 3858c2ecf20Sopenharmony_ci return error; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void pasemi_smb_remove(struct pci_dev *dev) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct pasemi_smbus *smbus = pci_get_drvdata(dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci i2c_del_adapter(&smbus->adapter); 3938c2ecf20Sopenharmony_ci release_region(smbus->base, smbus->size); 3948c2ecf20Sopenharmony_ci kfree(smbus); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic const struct pci_device_id pasemi_smb_ids[] = { 3988c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1959, 0xa003) }, 3998c2ecf20Sopenharmony_ci { 0, } 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pasemi_smb_ids); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic struct pci_driver pasemi_smb_driver = { 4058c2ecf20Sopenharmony_ci .name = "i2c-pasemi", 4068c2ecf20Sopenharmony_ci .id_table = pasemi_smb_ids, 4078c2ecf20Sopenharmony_ci .probe = pasemi_smb_probe, 4088c2ecf20Sopenharmony_ci .remove = pasemi_smb_remove, 4098c2ecf20Sopenharmony_ci}; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cimodule_pci_driver(pasemi_smb_driver); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4148c2ecf20Sopenharmony_ciMODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); 4158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PA Semi PWRficient SMBus driver"); 416