1/* 2 * Copyright (C) 2006-2007 PA Semi, Inc 3 * 4 * SMBus host driver for PA Semi PWRficient 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20#include <linux/module.h> 21#include <linux/pci.h> 22#include <linux/kernel.h> 23#include <linux/stddef.h> 24#include <linux/sched.h> 25#include <linux/i2c.h> 26#include <linux/delay.h> 27#include <linux/slab.h> 28#include <linux/io.h> 29 30static struct pci_driver pasemi_smb_driver; 31 32struct pasemi_smbus { 33 struct pci_dev *dev; 34 struct i2c_adapter adapter; 35 unsigned long base; 36 int size; 37}; 38 39/* Register offsets */ 40#define REG_MTXFIFO 0x00 41#define REG_MRXFIFO 0x04 42#define REG_SMSTA 0x14 43#define REG_CTL 0x1c 44 45/* Register defs */ 46#define MTXFIFO_READ 0x00000400 47#define MTXFIFO_STOP 0x00000200 48#define MTXFIFO_START 0x00000100 49#define MTXFIFO_DATA_M 0x000000ff 50 51#define MRXFIFO_EMPTY 0x00000100 52#define MRXFIFO_DATA_M 0x000000ff 53 54#define SMSTA_XEN 0x08000000 55#define SMSTA_MTN 0x00200000 56 57#define CTL_MRR 0x00000400 58#define CTL_MTR 0x00000200 59#define CTL_CLK_M 0x000000ff 60 61#define CLK_100K_DIV 84 62#define CLK_400K_DIV 21 63 64static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val) 65{ 66 dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n", 67 smbus->base + reg, val); 68 outl(val, smbus->base + reg); 69} 70 71static inline int reg_read(struct pasemi_smbus *smbus, int reg) 72{ 73 int ret; 74 ret = inl(smbus->base + reg); 75 dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n", 76 smbus->base + reg, ret); 77 return ret; 78} 79 80#define TXFIFO_WR(smbus, reg) reg_write((smbus), REG_MTXFIFO, (reg)) 81#define RXFIFO_RD(smbus) reg_read((smbus), REG_MRXFIFO) 82 83static void pasemi_smb_clear(struct pasemi_smbus *smbus) 84{ 85 unsigned int status; 86 87 status = reg_read(smbus, REG_SMSTA); 88 reg_write(smbus, REG_SMSTA, status); 89} 90 91static unsigned int pasemi_smb_waitready(struct pasemi_smbus *smbus) 92{ 93 int timeout = 10; 94 unsigned int status; 95 96 status = reg_read(smbus, REG_SMSTA); 97 98 while (!(status & SMSTA_XEN) && timeout--) { 99 msleep(1); 100 status = reg_read(smbus, REG_SMSTA); 101 } 102 103 /* Got NACK? */ 104 if (status & SMSTA_MTN) 105 return -ENXIO; 106 107 if (timeout < 0) { 108 dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status); 109 reg_write(smbus, REG_SMSTA, status); 110 return -ETIME; 111 } 112 113 /* Clear XEN */ 114 reg_write(smbus, REG_SMSTA, SMSTA_XEN); 115 116 return 0; 117} 118 119static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter, 120 struct i2c_msg *msg, int stop) 121{ 122 struct pasemi_smbus *smbus = adapter->algo_data; 123 int read, i, err; 124 u32 rd; 125 126 read = msg->flags & I2C_M_RD ? 1 : 0; 127 128 TXFIFO_WR(smbus, MTXFIFO_START | (msg->addr << 1) | read); 129 130 if (read) { 131 TXFIFO_WR(smbus, msg->len | MTXFIFO_READ | 132 (stop ? MTXFIFO_STOP : 0)); 133 134 err = pasemi_smb_waitready(smbus); 135 if (err) 136 goto reset_out; 137 138 for (i = 0; i < msg->len; i++) { 139 rd = RXFIFO_RD(smbus); 140 if (rd & MRXFIFO_EMPTY) { 141 err = -ENODATA; 142 goto reset_out; 143 } 144 msg->buf[i] = rd & MRXFIFO_DATA_M; 145 } 146 } else { 147 for (i = 0; i < msg->len - 1; i++) 148 TXFIFO_WR(smbus, msg->buf[i]); 149 150 TXFIFO_WR(smbus, msg->buf[msg->len-1] | 151 (stop ? MTXFIFO_STOP : 0)); 152 } 153 154 return 0; 155 156 reset_out: 157 reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 158 (CLK_100K_DIV & CTL_CLK_M))); 159 return err; 160} 161 162static int pasemi_i2c_xfer(struct i2c_adapter *adapter, 163 struct i2c_msg *msgs, int num) 164{ 165 struct pasemi_smbus *smbus = adapter->algo_data; 166 int ret, i; 167 168 pasemi_smb_clear(smbus); 169 170 ret = 0; 171 172 for (i = 0; i < num && !ret; i++) 173 ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1))); 174 175 return ret ? ret : num; 176} 177 178static int pasemi_smb_xfer(struct i2c_adapter *adapter, 179 u16 addr, unsigned short flags, char read_write, u8 command, 180 int size, union i2c_smbus_data *data) 181{ 182 struct pasemi_smbus *smbus = adapter->algo_data; 183 unsigned int rd; 184 int read_flag, err; 185 int len = 0, i; 186 187 /* All our ops take 8-bit shifted addresses */ 188 addr <<= 1; 189 read_flag = read_write == I2C_SMBUS_READ; 190 191 pasemi_smb_clear(smbus); 192 193 switch (size) { 194 case I2C_SMBUS_QUICK: 195 TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START | 196 MTXFIFO_STOP); 197 break; 198 case I2C_SMBUS_BYTE: 199 TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START); 200 if (read_write) 201 TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ); 202 else 203 TXFIFO_WR(smbus, MTXFIFO_STOP | command); 204 break; 205 case I2C_SMBUS_BYTE_DATA: 206 TXFIFO_WR(smbus, addr | MTXFIFO_START); 207 TXFIFO_WR(smbus, command); 208 if (read_write) { 209 TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 210 TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP); 211 } else { 212 TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte); 213 } 214 break; 215 case I2C_SMBUS_WORD_DATA: 216 TXFIFO_WR(smbus, addr | MTXFIFO_START); 217 TXFIFO_WR(smbus, command); 218 if (read_write) { 219 TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 220 TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP); 221 } else { 222 TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); 223 TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8)); 224 } 225 break; 226 case I2C_SMBUS_BLOCK_DATA: 227 TXFIFO_WR(smbus, addr | MTXFIFO_START); 228 TXFIFO_WR(smbus, command); 229 if (read_write) { 230 TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 231 TXFIFO_WR(smbus, 1 | MTXFIFO_READ); 232 rd = RXFIFO_RD(smbus); 233 len = min_t(u8, (rd & MRXFIFO_DATA_M), 234 I2C_SMBUS_BLOCK_MAX); 235 TXFIFO_WR(smbus, len | MTXFIFO_READ | 236 MTXFIFO_STOP); 237 } else { 238 len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX); 239 TXFIFO_WR(smbus, len); 240 for (i = 1; i < len; i++) 241 TXFIFO_WR(smbus, data->block[i]); 242 TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP); 243 } 244 break; 245 case I2C_SMBUS_PROC_CALL: 246 read_write = I2C_SMBUS_READ; 247 TXFIFO_WR(smbus, addr | MTXFIFO_START); 248 TXFIFO_WR(smbus, command); 249 TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); 250 TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M); 251 TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); 252 TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ); 253 break; 254 case I2C_SMBUS_BLOCK_PROC_CALL: 255 len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1); 256 read_write = I2C_SMBUS_READ; 257 TXFIFO_WR(smbus, addr | MTXFIFO_START); 258 TXFIFO_WR(smbus, command); 259 TXFIFO_WR(smbus, len); 260 for (i = 1; i <= len; i++) 261 TXFIFO_WR(smbus, data->block[i]); 262 TXFIFO_WR(smbus, addr | I2C_SMBUS_READ); 263 TXFIFO_WR(smbus, MTXFIFO_READ | 1); 264 rd = RXFIFO_RD(smbus); 265 len = min_t(u8, (rd & MRXFIFO_DATA_M), 266 I2C_SMBUS_BLOCK_MAX - len); 267 TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); 268 break; 269 270 default: 271 dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); 272 return -EINVAL; 273 } 274 275 err = pasemi_smb_waitready(smbus); 276 if (err) 277 goto reset_out; 278 279 if (read_write == I2C_SMBUS_WRITE) 280 return 0; 281 282 switch (size) { 283 case I2C_SMBUS_BYTE: 284 case I2C_SMBUS_BYTE_DATA: 285 rd = RXFIFO_RD(smbus); 286 if (rd & MRXFIFO_EMPTY) { 287 err = -ENODATA; 288 goto reset_out; 289 } 290 data->byte = rd & MRXFIFO_DATA_M; 291 break; 292 case I2C_SMBUS_WORD_DATA: 293 case I2C_SMBUS_PROC_CALL: 294 rd = RXFIFO_RD(smbus); 295 if (rd & MRXFIFO_EMPTY) { 296 err = -ENODATA; 297 goto reset_out; 298 } 299 data->word = rd & MRXFIFO_DATA_M; 300 rd = RXFIFO_RD(smbus); 301 if (rd & MRXFIFO_EMPTY) { 302 err = -ENODATA; 303 goto reset_out; 304 } 305 data->word |= (rd & MRXFIFO_DATA_M) << 8; 306 break; 307 case I2C_SMBUS_BLOCK_DATA: 308 case I2C_SMBUS_BLOCK_PROC_CALL: 309 data->block[0] = len; 310 for (i = 1; i <= len; i ++) { 311 rd = RXFIFO_RD(smbus); 312 if (rd & MRXFIFO_EMPTY) { 313 err = -ENODATA; 314 goto reset_out; 315 } 316 data->block[i] = rd & MRXFIFO_DATA_M; 317 } 318 break; 319 } 320 321 return 0; 322 323 reset_out: 324 reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 325 (CLK_100K_DIV & CTL_CLK_M))); 326 return err; 327} 328 329static u32 pasemi_smb_func(struct i2c_adapter *adapter) 330{ 331 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 332 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 333 I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | 334 I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C; 335} 336 337static const struct i2c_algorithm smbus_algorithm = { 338 .master_xfer = pasemi_i2c_xfer, 339 .smbus_xfer = pasemi_smb_xfer, 340 .functionality = pasemi_smb_func, 341}; 342 343static int __devinit pasemi_smb_probe(struct pci_dev *dev, 344 const struct pci_device_id *id) 345{ 346 struct pasemi_smbus *smbus; 347 int error; 348 349 if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) 350 return -ENODEV; 351 352 smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL); 353 if (!smbus) 354 return -ENOMEM; 355 356 smbus->dev = dev; 357 smbus->base = pci_resource_start(dev, 0); 358 smbus->size = pci_resource_len(dev, 0); 359 360 if (!request_region(smbus->base, smbus->size, 361 pasemi_smb_driver.name)) { 362 error = -EBUSY; 363 goto out_kfree; 364 } 365 366 smbus->adapter.owner = THIS_MODULE; 367 snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), 368 "PA Semi SMBus adapter at 0x%lx", smbus->base); 369 smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 370 smbus->adapter.algo = &smbus_algorithm; 371 smbus->adapter.algo_data = smbus; 372 smbus->adapter.nr = PCI_FUNC(dev->devfn); 373 374 /* set up the sysfs linkage to our parent device */ 375 smbus->adapter.dev.parent = &dev->dev; 376 377 reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | 378 (CLK_100K_DIV & CTL_CLK_M))); 379 380 error = i2c_add_numbered_adapter(&smbus->adapter); 381 if (error) 382 goto out_release_region; 383 384 pci_set_drvdata(dev, smbus); 385 386 return 0; 387 388 out_release_region: 389 release_region(smbus->base, smbus->size); 390 out_kfree: 391 kfree(smbus); 392 return error; 393} 394 395static void __devexit pasemi_smb_remove(struct pci_dev *dev) 396{ 397 struct pasemi_smbus *smbus = pci_get_drvdata(dev); 398 399 i2c_del_adapter(&smbus->adapter); 400 release_region(smbus->base, smbus->size); 401 kfree(smbus); 402} 403 404static const struct pci_device_id pasemi_smb_ids[] = { 405 { PCI_DEVICE(0x1959, 0xa003) }, 406 { 0, } 407}; 408 409MODULE_DEVICE_TABLE(pci, pasemi_smb_ids); 410 411static struct pci_driver pasemi_smb_driver = { 412 .name = "i2c-pasemi", 413 .id_table = pasemi_smb_ids, 414 .probe = pasemi_smb_probe, 415 .remove = __devexit_p(pasemi_smb_remove), 416}; 417 418static int __init pasemi_smb_init(void) 419{ 420 return pci_register_driver(&pasemi_smb_driver); 421} 422 423static void __exit pasemi_smb_exit(void) 424{ 425 pci_unregister_driver(&pasemi_smb_driver); 426} 427 428MODULE_LICENSE("GPL"); 429MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); 430MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver"); 431 432module_init(pasemi_smb_init); 433module_exit(pasemi_smb_exit); 434