Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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