| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU SMBus EEPROM device | 
					
						
							| 
									
										
										
										
											2007-09-16 21:08:06 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  |  * Copyright (c) 2007 Arastra, Inc. | 
					
						
							| 
									
										
										
										
											2007-09-16 21:08:06 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:30 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-04 15:40:22 +01:00
										 |  |  | #include "hw/hw.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-05 17:06:20 +01:00
										 |  |  | #include "hw/i2c/i2c.h"
 | 
					
						
							|  |  |  | #include "hw/i2c/smbus.h"
 | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | //#define DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct SMBusEEPROMDevice { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     SMBusDevice smbusdev; | 
					
						
							| 
									
										
										
										
											2009-08-03 17:35:33 +02:00
										 |  |  |     void *data; | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  |     uint8_t offset; | 
					
						
							|  |  |  | } SMBusEEPROMDevice; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2008-06-02 01:48:27 +00:00
										 |  |  |     printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void eeprom_send_byte(SMBusDevice *dev, uint8_t val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2008-06-02 01:48:27 +00:00
										 |  |  |     printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n", | 
					
						
							|  |  |  |            dev->i2c.address, val); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |     eeprom->offset = val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint8_t eeprom_receive_byte(SMBusDevice *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; | 
					
						
							| 
									
										
										
										
											2009-08-03 17:35:33 +02:00
										 |  |  |     uint8_t *data = eeprom->data; | 
					
						
							|  |  |  |     uint8_t val = data[eeprom->offset++]; | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2008-06-02 01:48:27 +00:00
										 |  |  |     printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n", | 
					
						
							|  |  |  |            dev->i2c.address, val); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |     return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-23 00:03:59 +00:00
										 |  |  | static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len) | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; | 
					
						
							| 
									
										
										
										
											2007-05-23 00:03:59 +00:00
										 |  |  |     int n; | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2008-06-02 01:48:27 +00:00
										 |  |  |     printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", | 
					
						
							|  |  |  |            dev->i2c.address, cmd, buf[0]); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-04-07 19:42:59 +02:00
										 |  |  |     /* A page write operation is not a valid SMBus command.
 | 
					
						
							| 
									
										
										
										
											2007-05-23 00:03:59 +00:00
										 |  |  |        It is a block write without a length byte.  Fortunately we | 
					
						
							|  |  |  |        get the full block anyway.  */ | 
					
						
							|  |  |  |     /* TODO: Should this set the current location?  */ | 
					
						
							|  |  |  |     if (cmd + len > 256) | 
					
						
							|  |  |  |         n = 256 - cmd; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         n = len; | 
					
						
							|  |  |  |     memcpy(eeprom->data + cmd, buf, n); | 
					
						
							|  |  |  |     len -= n; | 
					
						
							|  |  |  |     if (len) | 
					
						
							|  |  |  |         memcpy(eeprom->data, buf + n, len); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-23 00:03:59 +00:00
										 |  |  | static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; | 
					
						
							| 
									
										
										
										
											2007-05-23 00:03:59 +00:00
										 |  |  |     /* If this is the first byte then set the current position.  */ | 
					
						
							|  |  |  |     if (n == 0) | 
					
						
							|  |  |  |         eeprom->offset = cmd; | 
					
						
							|  |  |  |     /* As with writes, we implement block reads without the
 | 
					
						
							|  |  |  |        SMBus length byte.  */ | 
					
						
							|  |  |  |     return eeprom_receive_byte(dev); | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-05 11:07:06 +09:00
										 |  |  | static int smbus_eeprom_initfn(SMBusDevice *dev) | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; | 
					
						
							| 
									
										
										
										
											2007-09-17 08:09:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  |     eeprom->offset = 0; | 
					
						
							| 
									
										
										
										
											2009-08-14 10:36:05 +02:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2007-02-02 03:13:18 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  | static Property smbus_eeprom_properties[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-04 20:39:20 -06:00
										 |  |  | static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							| 
									
										
										
										
											2011-12-04 20:39:20 -06:00
										 |  |  |     SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sc->init = smbus_eeprom_initfn; | 
					
						
							|  |  |  |     sc->quick_cmd = eeprom_quick_cmd; | 
					
						
							|  |  |  |     sc->send_byte = eeprom_send_byte; | 
					
						
							|  |  |  |     sc->receive_byte = eeprom_receive_byte; | 
					
						
							|  |  |  |     sc->write_data = eeprom_write_data; | 
					
						
							|  |  |  |     sc->read_data = eeprom_read_data; | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     dc->props = smbus_eeprom_properties; | 
					
						
							| 
									
										
										
										
											2013-11-29 10:43:44 +01:00
										 |  |  |     /* Reason: pointer property "data" */ | 
					
						
							|  |  |  |     dc->cannot_instantiate_with_device_add_yet = true; | 
					
						
							| 
									
										
										
										
											2011-12-04 20:39:20 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-10 16:19:07 +01:00
										 |  |  | static const TypeInfo smbus_eeprom_info = { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .name          = "smbus-eeprom", | 
					
						
							|  |  |  |     .parent        = TYPE_SMBUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(SMBusEEPROMDevice), | 
					
						
							|  |  |  |     .class_init    = smbus_eeprom_class_initfn, | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | static void smbus_eeprom_register_types(void) | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     type_register_static(&smbus_eeprom_info); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | type_init(smbus_eeprom_register_types) | 
					
						
							| 
									
										
										
										
											2011-04-05 11:07:06 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-03 00:18:51 +02:00
										 |  |  | void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, | 
					
						
							| 
									
										
										
										
											2011-04-05 11:07:06 +09:00
										 |  |  |                        const uint8_t *eeprom_spd, int eeprom_spd_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int i; | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */ | 
					
						
							| 
									
										
										
										
											2011-04-05 11:07:06 +09:00
										 |  |  |     if (eeprom_spd_size > 0) { | 
					
						
							|  |  |  |         memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < nb_eeprom; i++) { | 
					
						
							|  |  |  |         DeviceState *eeprom; | 
					
						
							|  |  |  |         eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); | 
					
						
							|  |  |  |         qdev_prop_set_uint8(eeprom, "address", 0x50 + i); | 
					
						
							|  |  |  |         qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); | 
					
						
							|  |  |  |         qdev_init_nofail(eeprom); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |