brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · d7fcde6 Raw
423 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III3 *            platforms.4 *5 * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)6 */7 8#include <linux/module.h>9#include <linux/kernel.h>10#include <linux/types.h>11#include <linux/slab.h>12#include <linux/sched.h>13#include <linux/wait.h>14#include <linux/delay.h>15#include <linux/interrupt.h>16#include <linux/of.h>17#include <linux/of_platform.h>18#include <linux/platform_device.h>19#include <asm/bbc.h>20#include <asm/io.h>21 22#include "bbc_i2c.h"23 24/* Convert this driver to use i2c bus layer someday... */25#define I2C_PCF_PIN	0x8026#define I2C_PCF_ESO	0x4027#define I2C_PCF_ES1	0x2028#define I2C_PCF_ES2	0x1029#define I2C_PCF_ENI	0x0830#define I2C_PCF_STA	0x0431#define I2C_PCF_STO	0x0232#define I2C_PCF_ACK	0x0133 34#define I2C_PCF_START    (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK)35#define I2C_PCF_STOP     (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)36#define I2C_PCF_REPSTART (              I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)37#define I2C_PCF_IDLE     (I2C_PCF_PIN | I2C_PCF_ESO               | I2C_PCF_ACK)38 39#define I2C_PCF_INI 0x40   /* 1 if not initialized */40#define I2C_PCF_STS 0x2041#define I2C_PCF_BER 0x1042#define I2C_PCF_AD0 0x0843#define I2C_PCF_LRB 0x0844#define I2C_PCF_AAS 0x0445#define I2C_PCF_LAB 0x0246#define I2C_PCF_BB  0x0147 48/* The BBC devices have two I2C controllers.  The first I2C controller49 * connects mainly to configuration proms (NVRAM, cpu configuration,50 * dimm types, etc.).  Whereas the second I2C controller connects to51 * environmental control devices such as fans and temperature sensors.52 * The second controller also connects to the smartcard reader, if present.53 */54 55static void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val)56{57	int i;58 59	for (i = 0; i < NUM_CHILDREN; i++) {60		if (bp->devs[i].device == op) {61			bp->devs[i].client_claimed = val;62			return;63		}64	}65}66 67#define claim_device(BP,ECHILD)		set_device_claimage(BP,ECHILD,1)68#define release_device(BP,ECHILD)	set_device_claimage(BP,ECHILD,0)69 70struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)71{72	struct platform_device *op = NULL;73	int curidx = 0, i;74 75	for (i = 0; i < NUM_CHILDREN; i++) {76		if (!(op = bp->devs[i].device))77			break;78		if (curidx == index)79			goto out;80		op = NULL;81		curidx++;82	}83 84out:85	if (curidx == index)86		return op;87	return NULL;88}89 90struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op)91{92	struct bbc_i2c_client *client;93	const u32 *reg;94 95	client = kzalloc(sizeof(*client), GFP_KERNEL);96	if (!client)97		return NULL;98	client->bp = bp;99	client->op = op;100 101	reg = of_get_property(op->dev.of_node, "reg", NULL);102	if (!reg) {103		kfree(client);104		return NULL;105	}106 107	client->bus = reg[0];108	client->address = reg[1];109 110	claim_device(bp, op);111 112	return client;113}114 115void bbc_i2c_detach(struct bbc_i2c_client *client)116{117	struct bbc_i2c_bus *bp = client->bp;118	struct platform_device *op = client->op;119 120	release_device(bp, op);121	kfree(client);122}123 124static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status)125{126	DECLARE_WAITQUEUE(wait, current);127	int limit = 32;128	int ret = 1;129 130	bp->waiting = 1;131	add_wait_queue(&bp->wq, &wait);132	while (limit-- > 0) {133		long val;134 135		val = wait_event_interruptible_timeout(136				bp->wq,137				(((*status = readb(bp->i2c_control_regs + 0))138				  & I2C_PCF_PIN) == 0),139				msecs_to_jiffies(250));140		if (val > 0) {141			ret = 0;142			break;143		}144	}145	remove_wait_queue(&bp->wq, &wait);146	bp->waiting = 0;147 148	return ret;149}150 151int bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off)152{153	struct bbc_i2c_bus *bp = client->bp;154	int address = client->address;155	u8 status;156	int ret = -1;157 158	if (bp->i2c_bussel_reg != NULL)159		writeb(client->bus, bp->i2c_bussel_reg);160 161	writeb(address, bp->i2c_control_regs + 0x1);162	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);163	if (wait_for_pin(bp, &status))164		goto out;165 166	writeb(off, bp->i2c_control_regs + 0x1);167	if (wait_for_pin(bp, &status) ||168	    (status & I2C_PCF_LRB) != 0)169		goto out;170 171	writeb(val, bp->i2c_control_regs + 0x1);172	if (wait_for_pin(bp, &status))173		goto out;174 175	ret = 0;176 177out:178	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);179	return ret;180}181 182int bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off)183{184	struct bbc_i2c_bus *bp = client->bp;185	unsigned char address = client->address, status;186	int ret = -1;187 188	if (bp->i2c_bussel_reg != NULL)189		writeb(client->bus, bp->i2c_bussel_reg);190 191	writeb(address, bp->i2c_control_regs + 0x1);192	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);193	if (wait_for_pin(bp, &status))194		goto out;195 196	writeb(off, bp->i2c_control_regs + 0x1);197	if (wait_for_pin(bp, &status) ||198	    (status & I2C_PCF_LRB) != 0)199		goto out;200 201	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);202 203	address |= 0x1; /* READ */204 205	writeb(address, bp->i2c_control_regs + 0x1);206	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);207	if (wait_for_pin(bp, &status))208		goto out;209 210	/* Set PIN back to one so the device sends the first211	 * byte.212	 */213	(void) readb(bp->i2c_control_regs + 0x1);214	if (wait_for_pin(bp, &status))215		goto out;216 217	writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0);218	*byte = readb(bp->i2c_control_regs + 0x1);219	if (wait_for_pin(bp, &status))220		goto out;221 222	ret = 0;223 224out:225	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);226	(void) readb(bp->i2c_control_regs + 0x1);227 228	return ret;229}230 231int bbc_i2c_write_buf(struct bbc_i2c_client *client,232		      char *buf, int len, int off)233{234	int ret = 0;235 236	while (len > 0) {237		ret = bbc_i2c_writeb(client, *buf, off);238		if (ret < 0)239			break;240		len--;241		buf++;242		off++;243	}244	return ret;245}246 247int bbc_i2c_read_buf(struct bbc_i2c_client *client,248		     char *buf, int len, int off)249{250	int ret = 0;251 252	while (len > 0) {253		ret = bbc_i2c_readb(client, buf, off);254		if (ret < 0)255			break;256		len--;257		buf++;258		off++;259	}260 261	return ret;262}263 264EXPORT_SYMBOL(bbc_i2c_getdev);265EXPORT_SYMBOL(bbc_i2c_attach);266EXPORT_SYMBOL(bbc_i2c_detach);267EXPORT_SYMBOL(bbc_i2c_writeb);268EXPORT_SYMBOL(bbc_i2c_readb);269EXPORT_SYMBOL(bbc_i2c_write_buf);270EXPORT_SYMBOL(bbc_i2c_read_buf);271 272static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id)273{274	struct bbc_i2c_bus *bp = dev_id;275 276	/* PIN going from set to clear is the only event which277	 * makes the i2c assert an interrupt.278	 */279	if (bp->waiting &&280	    !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN))281		wake_up_interruptible(&bp->wq);282 283	return IRQ_HANDLED;284}285 286static void reset_one_i2c(struct bbc_i2c_bus *bp)287{288	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);289	writeb(bp->own, bp->i2c_control_regs + 0x1);290	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);291	writeb(bp->clock, bp->i2c_control_regs + 0x1);292	writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);293}294 295static struct bbc_i2c_bus * attach_one_i2c(struct platform_device *op, int index)296{297	struct bbc_i2c_bus *bp;298	struct device_node *dp;299	int entry;300 301	bp = kzalloc(sizeof(*bp), GFP_KERNEL);302	if (!bp)303		return NULL;304 305	INIT_LIST_HEAD(&bp->temps);306	INIT_LIST_HEAD(&bp->fans);307 308	bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs");309	if (!bp->i2c_control_regs)310		goto fail;311 312	if (op->num_resources == 2) {313		bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel");314		if (!bp->i2c_bussel_reg)315			goto fail;316	}317 318	bp->waiting = 0;319	init_waitqueue_head(&bp->wq);320	if (request_irq(op->archdata.irqs[0], bbc_i2c_interrupt,321			IRQF_SHARED, "bbc_i2c", bp))322		goto fail;323 324	bp->index = index;325	bp->op = op;326 327	spin_lock_init(&bp->lock);328 329	entry = 0;330	for (dp = op->dev.of_node->child;331	     dp && entry < 8;332	     dp = dp->sibling, entry++) {333		struct platform_device *child_op;334 335		child_op = of_find_device_by_node(dp);336		bp->devs[entry].device = child_op;337		bp->devs[entry].client_claimed = 0;338	}339 340	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);341	bp->own = readb(bp->i2c_control_regs + 0x01);342	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);343	bp->clock = readb(bp->i2c_control_regs + 0x01);344 345	printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n",346	       bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock);347 348	reset_one_i2c(bp);349 350	return bp;351 352fail:353	if (bp->i2c_bussel_reg)354		of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1);355	if (bp->i2c_control_regs)356		of_iounmap(&op->resource[0], bp->i2c_control_regs, 2);357	kfree(bp);358	return NULL;359}360 361static int bbc_i2c_probe(struct platform_device *op)362{363	struct bbc_i2c_bus *bp;364	int err, index = 0;365 366	bp = attach_one_i2c(op, index);367	if (!bp)368		return -EINVAL;369 370	err = bbc_envctrl_init(bp);371	if (err) {372		free_irq(op->archdata.irqs[0], bp);373		if (bp->i2c_bussel_reg)374			of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);375		if (bp->i2c_control_regs)376			of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);377		kfree(bp);378	} else {379		dev_set_drvdata(&op->dev, bp);380	}381 382	return err;383}384 385static void bbc_i2c_remove(struct platform_device *op)386{387	struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev);388 389	bbc_envctrl_cleanup(bp);390 391	free_irq(op->archdata.irqs[0], bp);392 393	if (bp->i2c_bussel_reg)394		of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);395	if (bp->i2c_control_regs)396		of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);397 398	kfree(bp);399}400 401static const struct of_device_id bbc_i2c_match[] = {402	{403		.name = "i2c",404		.compatible = "SUNW,bbc-i2c",405	},406	{},407};408MODULE_DEVICE_TABLE(of, bbc_i2c_match);409 410static struct platform_driver bbc_i2c_driver = {411	.driver = {412		.name = "bbc_i2c",413		.of_match_table = bbc_i2c_match,414	},415	.probe		= bbc_i2c_probe,416	.remove_new	= bbc_i2c_remove,417};418 419module_platform_driver(bbc_i2c_driver);420 421MODULE_DESCRIPTION("UltraSPARC-III bootbus i2c controller driver");422MODULE_LICENSE("GPL");423