brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · b6b0353 Raw
450 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3    i2c Support for Apple SMU Controller4 5    Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.6                       <benh@kernel.crashing.org>7 8 9*/10 11#include <linux/module.h>12#include <linux/kernel.h>13#include <linux/types.h>14#include <linux/i2c.h>15#include <linux/device.h>16#include <linux/platform_device.h>17#include <linux/of_irq.h>18 19#include <asm/pmac_low_i2c.h>20 21MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");22MODULE_DESCRIPTION("I2C driver for Apple PowerMac");23MODULE_LICENSE("GPL");24 25/*26 * SMBUS-type transfer entrypoint27 */28static s32 i2c_powermac_smbus_xfer(	struct i2c_adapter*	adap,29					u16			addr,30					unsigned short		flags,31					char			read_write,32					u8			command,33					int			size,34					union i2c_smbus_data*	data)35{36	struct pmac_i2c_bus	*bus = i2c_get_adapdata(adap);37	int			rc = 0;38	int			read = (read_write == I2C_SMBUS_READ);39	int			addrdir = (addr << 1) | read;40	int			mode, subsize, len;41	u32			subaddr;42	u8			*buf;43	u8			local[2];44 45	if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) {46		mode = pmac_i2c_mode_std;47		subsize = 0;48		subaddr = 0;49	} else {50		mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub;51		subsize = 1;52		subaddr = command;53	}54 55	switch (size) {56        case I2C_SMBUS_QUICK:57		buf = NULL;58		len = 0;59	    	break;60        case I2C_SMBUS_BYTE:61        case I2C_SMBUS_BYTE_DATA:62		buf = &data->byte;63		len = 1;64	    	break;65        case I2C_SMBUS_WORD_DATA:66		if (!read) {67			local[0] = data->word & 0xff;68			local[1] = (data->word >> 8) & 0xff;69		}70		buf = local;71		len = 2;72	    	break;73 74	/* Note that these are broken vs. the expected smbus API where75	 * on reads, the length is actually returned from the function,76	 * but I think the current API makes no sense and I don't want77	 * any driver that I haven't verified for correctness to go78	 * anywhere near a pmac i2c bus anyway ...79	 */80        case I2C_SMBUS_BLOCK_DATA:81		buf = data->block;82		len = data->block[0] + 1;83		break;84	case I2C_SMBUS_I2C_BLOCK_DATA:85		buf = &data->block[1];86		len = data->block[0];87		break;88 89        default:90		return -EINVAL;91	}92 93	rc = pmac_i2c_open(bus, 0);94	if (rc) {95		dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);96		return rc;97	}98 99	rc = pmac_i2c_setmode(bus, mode);100	if (rc) {101		dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",102			mode, rc);103		goto bail;104	}105 106	rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len);107	if (rc) {108		if (rc == -ENXIO)109			dev_dbg(&adap->dev,110				"I2C transfer at 0x%02x failed, size %d, "111				"err %d\n", addrdir >> 1, size, rc);112		else113			dev_err(&adap->dev,114				"I2C transfer at 0x%02x failed, size %d, "115				"err %d\n", addrdir >> 1, size, rc);116		goto bail;117	}118 119	if (size == I2C_SMBUS_WORD_DATA && read) {120		data->word = ((u16)local[1]) << 8;121		data->word |= local[0];122	}123 124 bail:125	pmac_i2c_close(bus);126	return rc;127}128 129/*130 * Generic i2c transfer entrypoint. This driver only supports single131 * messages (for "lame i2c" transfers). Anything else should use the smbus132 * entry point133 */134static int i2c_powermac_xfer(struct i2c_adapter *adap,135			     struct i2c_msg *msgs,136			     int num)137{138	struct pmac_i2c_bus	*bus = i2c_get_adapdata(adap);139	int			rc = 0;140	int			addrdir;141 142	if (msgs->flags & I2C_M_TEN)143		return -EINVAL;144	addrdir = i2c_8bit_addr_from_msg(msgs);145 146	rc = pmac_i2c_open(bus, 0);147	if (rc) {148		dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);149		return rc;150	}151	rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);152	if (rc) {153		dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",154			pmac_i2c_mode_std, rc);155		goto bail;156	}157	rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len);158	if (rc < 0) {159		if (rc == -ENXIO)160			dev_dbg(&adap->dev, "I2C %s 0x%02x failed, err %d\n",161				addrdir & 1 ? "read from" : "write to",162				addrdir >> 1, rc);163		else164			dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n",165				addrdir & 1 ? "read from" : "write to",166				addrdir >> 1, rc);167	}168 bail:169	pmac_i2c_close(bus);170	return rc < 0 ? rc : 1;171}172 173static u32 i2c_powermac_func(struct i2c_adapter * adapter)174{175	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |176		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |177		I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C;178}179 180/* For now, we only handle smbus */181static const struct i2c_algorithm i2c_powermac_algorithm = {182	.smbus_xfer = i2c_powermac_smbus_xfer,183	.xfer = i2c_powermac_xfer,184	.functionality = i2c_powermac_func,185};186 187static const struct i2c_adapter_quirks i2c_powermac_quirks = {188	.max_num_msgs = 1,189};190 191static void i2c_powermac_remove(struct platform_device *dev)192{193	struct i2c_adapter	*adapter = platform_get_drvdata(dev);194 195	i2c_del_adapter(adapter);196	memset(adapter, 0, sizeof(*adapter));197}198 199static u32 i2c_powermac_get_addr(struct i2c_adapter *adap,200					   struct pmac_i2c_bus *bus,201					   struct device_node *node)202{203	u32 prop;204	int ret;205 206	/* First check for valid "reg" */207	ret = of_property_read_u32(node, "reg", &prop);208	if (ret == 0)209		return (prop & 0xff) >> 1;210 211	/* Then check old-style "i2c-address" */212	ret = of_property_read_u32(node, "i2c-address", &prop);213	if (ret == 0)214		return (prop & 0xff) >> 1;215 216	/* Now handle some devices with missing "reg" properties */217	if (of_node_name_eq(node, "cereal"))218		return 0x60;219	else if (of_node_name_eq(node, "deq"))220		return 0x34;221 222	dev_warn(&adap->dev, "No i2c address for %pOF\n", node);223 224	return 0xffffffff;225}226 227static void i2c_powermac_create_one(struct i2c_adapter *adap,228					      const char *type,229					      u32 addr)230{231	struct i2c_board_info info = {};232	struct i2c_client *newdev;233 234	strscpy(info.type, type, sizeof(info.type));235	info.addr = addr;236	newdev = i2c_new_client_device(adap, &info);237	if (IS_ERR(newdev))238		dev_err(&adap->dev,239			"i2c-powermac: Failure to register missing %s\n",240			type);241}242 243static void i2c_powermac_add_missing(struct i2c_adapter *adap,244					       struct pmac_i2c_bus *bus,245					       bool found_onyx)246{247	struct device_node *busnode = pmac_i2c_get_bus_node(bus);248	int rc;249 250	/* Check for the onyx audio codec */251#define ONYX_REG_CONTROL		67252	if (of_device_is_compatible(busnode, "k2-i2c") && !found_onyx) {253		union i2c_smbus_data data;254 255		rc = i2c_smbus_xfer(adap, 0x46, 0, I2C_SMBUS_READ,256				    ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA,257				    &data);258		if (rc >= 0)259			i2c_powermac_create_one(adap, "MAC,pcm3052", 0x46);260 261		rc = i2c_smbus_xfer(adap, 0x47, 0, I2C_SMBUS_READ,262				    ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA,263				    &data);264		if (rc >= 0)265			i2c_powermac_create_one(adap, "MAC,pcm3052", 0x47);266	}267}268 269static bool i2c_powermac_get_type(struct i2c_adapter *adap,270					    struct device_node *node,271					    u32 addr, char *type, int type_size)272{273	char tmp[16];274 275	/*276	 * Note: we do _NOT_ want the standard i2c drivers to match with any of277	 * our powermac stuff unless they have been specifically modified to278	 * handle it on a case by case basis. For example, for thermal control,279	 * things like lm75 etc... shall match with their corresponding280	 * windfarm drivers, _NOT_ the generic ones, so we force a prefix of281	 * 'MAC', onto the modalias to make that happen282	 */283 284	/* First try proper modalias */285	if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) {286		snprintf(type, type_size, "MAC,%s", tmp);287		return true;288	}289 290	/* Now look for known workarounds */291	if (of_node_name_eq(node, "deq")) {292		/* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */293		if (addr == 0x34) {294			snprintf(type, type_size, "MAC,tas3001");295			return true;296		} else if (addr == 0x35) {297			snprintf(type, type_size, "MAC,tas3004");298			return true;299		}300	}301 302	dev_err(&adap->dev, "i2c-powermac: modalias failure on %pOF\n", node);303	return false;304}305 306static void i2c_powermac_register_devices(struct i2c_adapter *adap,307						    struct pmac_i2c_bus *bus)308{309	struct i2c_client *newdev;310	struct device_node *node;311	bool found_onyx = false;312 313	/*314	 * In some cases we end up with the via-pmu node itself, in this315	 * case we skip this function completely as the device-tree will316	 * not contain anything useful.317	 */318	if (of_node_name_eq(adap->dev.of_node, "via-pmu"))319		return;320 321	for_each_child_of_node(adap->dev.of_node, node) {322		struct i2c_board_info info = {};323		u32 addr;324 325		/* Get address & channel */326		addr = i2c_powermac_get_addr(adap, bus, node);327		if (addr == 0xffffffff)328			continue;329 330		/* Multibus setup, check channel */331		if (!pmac_i2c_match_adapter(node, adap))332			continue;333 334		dev_dbg(&adap->dev, "i2c-powermac: register %pOF\n", node);335 336		/*337		 * Keep track of some device existence to handle338		 * workarounds later.339		 */340		if (of_device_is_compatible(node, "pcm3052"))341			found_onyx = true;342 343		/* Make up a modalias */344		if (!i2c_powermac_get_type(adap, node, addr,345					   info.type, sizeof(info.type))) {346			continue;347		}348 349		/* Fill out the rest of the info structure */350		info.addr = addr;351		info.irq = irq_of_parse_and_map(node, 0);352		info.of_node = of_node_get(node);353 354		newdev = i2c_new_client_device(adap, &info);355		if (IS_ERR(newdev)) {356			dev_err(&adap->dev, "i2c-powermac: Failure to register"357				" %pOF\n", node);358			of_node_put(node);359			/* We do not dispose of the interrupt mapping on360			 * purpose. It's not necessary (interrupt cannot be361			 * re-used) and somebody else might have grabbed it362			 * via direct DT lookup so let's not bother363			 */364			continue;365		}366	}367 368	/* Additional workarounds */369	i2c_powermac_add_missing(adap, bus, found_onyx);370}371 372static int i2c_powermac_probe(struct platform_device *dev)373{374	struct pmac_i2c_bus *bus = dev_get_platdata(&dev->dev);375	struct device_node *parent;376	struct i2c_adapter *adapter;377	int rc;378 379	if (bus == NULL)380		return -EINVAL;381	adapter = pmac_i2c_get_adapter(bus);382 383	/* Ok, now we need to make up a name for the interface that will384	 * match what we used to do in the past, that is basically the385	 * controller's parent device node for keywest. PMU didn't have a386	 * naming convention and SMU has a different one387	 */388	switch(pmac_i2c_get_type(bus)) {389	case pmac_i2c_bus_keywest:390		parent = of_get_parent(pmac_i2c_get_controller(bus));391		if (parent == NULL)392			return -EINVAL;393		snprintf(adapter->name, sizeof(adapter->name), "%pOFn %d",394			 parent,395			 pmac_i2c_get_channel(bus));396		of_node_put(parent);397		break;398	case pmac_i2c_bus_pmu:399		snprintf(adapter->name, sizeof(adapter->name), "pmu %d",400			 pmac_i2c_get_channel(bus));401		break;402	case pmac_i2c_bus_smu:403		/* This is not what we used to do but I'm fixing drivers at404		 * the same time as this change405		 */406		snprintf(adapter->name, sizeof(adapter->name), "smu %d",407			 pmac_i2c_get_channel(bus));408		break;409	default:410		return -EINVAL;411	}412 413	platform_set_drvdata(dev, adapter);414	adapter->algo = &i2c_powermac_algorithm;415	adapter->quirks = &i2c_powermac_quirks;416	i2c_set_adapdata(adapter, bus);417	adapter->dev.parent = &dev->dev;418 419	/* Clear of_node to skip automatic registration of i2c child nodes */420	adapter->dev.of_node = NULL;421	rc = i2c_add_adapter(adapter);422	if (rc) {423		printk(KERN_ERR "i2c-powermac: Adapter %s registration "424		       "failed\n", adapter->name);425		memset(adapter, 0, sizeof(*adapter));426		return rc;427	}428 429	printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name);430 431	/* Use custom child registration due to Apple device-tree funkyness */432	adapter->dev.of_node = dev->dev.of_node;433	i2c_powermac_register_devices(adapter, bus);434 435	return 0;436}437 438static struct platform_driver i2c_powermac_driver = {439	.probe = i2c_powermac_probe,440	.remove_new = i2c_powermac_remove,441	.driver = {442		.name = "i2c-powermac",443		.bus = &platform_bus_type,444	},445};446 447module_platform_driver(i2c_powermac_driver);448 449MODULE_ALIAS("platform:i2c-powermac");450