162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III 362306a36Sopenharmony_ci * platforms. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/wait.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_platform.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <asm/bbc.h> 2062306a36Sopenharmony_ci#include <asm/io.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "bbc_i2c.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Convert this driver to use i2c bus layer someday... */ 2562306a36Sopenharmony_ci#define I2C_PCF_PIN 0x80 2662306a36Sopenharmony_ci#define I2C_PCF_ESO 0x40 2762306a36Sopenharmony_ci#define I2C_PCF_ES1 0x20 2862306a36Sopenharmony_ci#define I2C_PCF_ES2 0x10 2962306a36Sopenharmony_ci#define I2C_PCF_ENI 0x08 3062306a36Sopenharmony_ci#define I2C_PCF_STA 0x04 3162306a36Sopenharmony_ci#define I2C_PCF_STO 0x02 3262306a36Sopenharmony_ci#define I2C_PCF_ACK 0x01 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK) 3562306a36Sopenharmony_ci#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) 3662306a36Sopenharmony_ci#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) 3762306a36Sopenharmony_ci#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define I2C_PCF_INI 0x40 /* 1 if not initialized */ 4062306a36Sopenharmony_ci#define I2C_PCF_STS 0x20 4162306a36Sopenharmony_ci#define I2C_PCF_BER 0x10 4262306a36Sopenharmony_ci#define I2C_PCF_AD0 0x08 4362306a36Sopenharmony_ci#define I2C_PCF_LRB 0x08 4462306a36Sopenharmony_ci#define I2C_PCF_AAS 0x04 4562306a36Sopenharmony_ci#define I2C_PCF_LAB 0x02 4662306a36Sopenharmony_ci#define I2C_PCF_BB 0x01 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* The BBC devices have two I2C controllers. The first I2C controller 4962306a36Sopenharmony_ci * connects mainly to configuration proms (NVRAM, cpu configuration, 5062306a36Sopenharmony_ci * dimm types, etc.). Whereas the second I2C controller connects to 5162306a36Sopenharmony_ci * environmental control devices such as fans and temperature sensors. 5262306a36Sopenharmony_ci * The second controller also connects to the smartcard reader, if present. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < NUM_CHILDREN; i++) { 6062306a36Sopenharmony_ci if (bp->devs[i].device == op) { 6162306a36Sopenharmony_ci bp->devs[i].client_claimed = val; 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) 6862306a36Sopenharmony_ci#define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct platform_device *op = NULL; 7362306a36Sopenharmony_ci int curidx = 0, i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (i = 0; i < NUM_CHILDREN; i++) { 7662306a36Sopenharmony_ci if (!(op = bp->devs[i].device)) 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci if (curidx == index) 7962306a36Sopenharmony_ci goto out; 8062306a36Sopenharmony_ci op = NULL; 8162306a36Sopenharmony_ci curidx++; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciout: 8562306a36Sopenharmony_ci if (curidx == index) 8662306a36Sopenharmony_ci return op; 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct bbc_i2c_client *client; 9362306a36Sopenharmony_ci const u32 *reg; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci client = kzalloc(sizeof(*client), GFP_KERNEL); 9662306a36Sopenharmony_ci if (!client) 9762306a36Sopenharmony_ci return NULL; 9862306a36Sopenharmony_ci client->bp = bp; 9962306a36Sopenharmony_ci client->op = op; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci reg = of_get_property(op->dev.of_node, "reg", NULL); 10262306a36Sopenharmony_ci if (!reg) { 10362306a36Sopenharmony_ci kfree(client); 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci client->bus = reg[0]; 10862306a36Sopenharmony_ci client->address = reg[1]; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci claim_device(bp, op); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return client; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid bbc_i2c_detach(struct bbc_i2c_client *client) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct bbc_i2c_bus *bp = client->bp; 11862306a36Sopenharmony_ci struct platform_device *op = client->op; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci release_device(bp, op); 12162306a36Sopenharmony_ci kfree(client); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 12762306a36Sopenharmony_ci int limit = 32; 12862306a36Sopenharmony_ci int ret = 1; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci bp->waiting = 1; 13162306a36Sopenharmony_ci add_wait_queue(&bp->wq, &wait); 13262306a36Sopenharmony_ci while (limit-- > 0) { 13362306a36Sopenharmony_ci long val; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci val = wait_event_interruptible_timeout( 13662306a36Sopenharmony_ci bp->wq, 13762306a36Sopenharmony_ci (((*status = readb(bp->i2c_control_regs + 0)) 13862306a36Sopenharmony_ci & I2C_PCF_PIN) == 0), 13962306a36Sopenharmony_ci msecs_to_jiffies(250)); 14062306a36Sopenharmony_ci if (val > 0) { 14162306a36Sopenharmony_ci ret = 0; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci remove_wait_queue(&bp->wq, &wait); 14662306a36Sopenharmony_ci bp->waiting = 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return ret; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct bbc_i2c_bus *bp = client->bp; 15462306a36Sopenharmony_ci int address = client->address; 15562306a36Sopenharmony_ci u8 status; 15662306a36Sopenharmony_ci int ret = -1; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (bp->i2c_bussel_reg != NULL) 15962306a36Sopenharmony_ci writeb(client->bus, bp->i2c_bussel_reg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci writeb(address, bp->i2c_control_regs + 0x1); 16262306a36Sopenharmony_ci writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); 16362306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 16462306a36Sopenharmony_ci goto out; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci writeb(off, bp->i2c_control_regs + 0x1); 16762306a36Sopenharmony_ci if (wait_for_pin(bp, &status) || 16862306a36Sopenharmony_ci (status & I2C_PCF_LRB) != 0) 16962306a36Sopenharmony_ci goto out; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci writeb(val, bp->i2c_control_regs + 0x1); 17262306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ret = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciout: 17862306a36Sopenharmony_ci writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct bbc_i2c_bus *bp = client->bp; 18562306a36Sopenharmony_ci unsigned char address = client->address, status; 18662306a36Sopenharmony_ci int ret = -1; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (bp->i2c_bussel_reg != NULL) 18962306a36Sopenharmony_ci writeb(client->bus, bp->i2c_bussel_reg); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci writeb(address, bp->i2c_control_regs + 0x1); 19262306a36Sopenharmony_ci writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); 19362306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci writeb(off, bp->i2c_control_regs + 0x1); 19762306a36Sopenharmony_ci if (wait_for_pin(bp, &status) || 19862306a36Sopenharmony_ci (status & I2C_PCF_LRB) != 0) 19962306a36Sopenharmony_ci goto out; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci address |= 0x1; /* READ */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci writeb(address, bp->i2c_control_regs + 0x1); 20662306a36Sopenharmony_ci writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0); 20762306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 20862306a36Sopenharmony_ci goto out; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Set PIN back to one so the device sends the first 21162306a36Sopenharmony_ci * byte. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci (void) readb(bp->i2c_control_regs + 0x1); 21462306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0); 21862306a36Sopenharmony_ci *byte = readb(bp->i2c_control_regs + 0x1); 21962306a36Sopenharmony_ci if (wait_for_pin(bp, &status)) 22062306a36Sopenharmony_ci goto out; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciout: 22562306a36Sopenharmony_ci writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0); 22662306a36Sopenharmony_ci (void) readb(bp->i2c_control_regs + 0x1); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciint bbc_i2c_write_buf(struct bbc_i2c_client *client, 23262306a36Sopenharmony_ci char *buf, int len, int off) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int ret = 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci while (len > 0) { 23762306a36Sopenharmony_ci ret = bbc_i2c_writeb(client, *buf, off); 23862306a36Sopenharmony_ci if (ret < 0) 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci len--; 24162306a36Sopenharmony_ci buf++; 24262306a36Sopenharmony_ci off++; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint bbc_i2c_read_buf(struct bbc_i2c_client *client, 24862306a36Sopenharmony_ci char *buf, int len, int off) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int ret = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci while (len > 0) { 25362306a36Sopenharmony_ci ret = bbc_i2c_readb(client, buf, off); 25462306a36Sopenharmony_ci if (ret < 0) 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci len--; 25762306a36Sopenharmony_ci buf++; 25862306a36Sopenharmony_ci off++; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_getdev); 26562306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_attach); 26662306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_detach); 26762306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_writeb); 26862306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_readb); 26962306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_write_buf); 27062306a36Sopenharmony_ciEXPORT_SYMBOL(bbc_i2c_read_buf); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct bbc_i2c_bus *bp = dev_id; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* PIN going from set to clear is the only event which 27762306a36Sopenharmony_ci * makes the i2c assert an interrupt. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci if (bp->waiting && 28062306a36Sopenharmony_ci !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN)) 28162306a36Sopenharmony_ci wake_up_interruptible(&bp->wq); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return IRQ_HANDLED; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void reset_one_i2c(struct bbc_i2c_bus *bp) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); 28962306a36Sopenharmony_ci writeb(bp->own, bp->i2c_control_regs + 0x1); 29062306a36Sopenharmony_ci writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); 29162306a36Sopenharmony_ci writeb(bp->clock, bp->i2c_control_regs + 0x1); 29262306a36Sopenharmony_ci writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic struct bbc_i2c_bus * attach_one_i2c(struct platform_device *op, int index) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct bbc_i2c_bus *bp; 29862306a36Sopenharmony_ci struct device_node *dp; 29962306a36Sopenharmony_ci int entry; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci bp = kzalloc(sizeof(*bp), GFP_KERNEL); 30262306a36Sopenharmony_ci if (!bp) 30362306a36Sopenharmony_ci return NULL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci INIT_LIST_HEAD(&bp->temps); 30662306a36Sopenharmony_ci INIT_LIST_HEAD(&bp->fans); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs"); 30962306a36Sopenharmony_ci if (!bp->i2c_control_regs) 31062306a36Sopenharmony_ci goto fail; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (op->num_resources == 2) { 31362306a36Sopenharmony_ci bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel"); 31462306a36Sopenharmony_ci if (!bp->i2c_bussel_reg) 31562306a36Sopenharmony_ci goto fail; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci bp->waiting = 0; 31962306a36Sopenharmony_ci init_waitqueue_head(&bp->wq); 32062306a36Sopenharmony_ci if (request_irq(op->archdata.irqs[0], bbc_i2c_interrupt, 32162306a36Sopenharmony_ci IRQF_SHARED, "bbc_i2c", bp)) 32262306a36Sopenharmony_ci goto fail; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci bp->index = index; 32562306a36Sopenharmony_ci bp->op = op; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock_init(&bp->lock); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci entry = 0; 33062306a36Sopenharmony_ci for (dp = op->dev.of_node->child; 33162306a36Sopenharmony_ci dp && entry < 8; 33262306a36Sopenharmony_ci dp = dp->sibling, entry++) { 33362306a36Sopenharmony_ci struct platform_device *child_op; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci child_op = of_find_device_by_node(dp); 33662306a36Sopenharmony_ci bp->devs[entry].device = child_op; 33762306a36Sopenharmony_ci bp->devs[entry].client_claimed = 0; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0); 34162306a36Sopenharmony_ci bp->own = readb(bp->i2c_control_regs + 0x01); 34262306a36Sopenharmony_ci writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0); 34362306a36Sopenharmony_ci bp->clock = readb(bp->i2c_control_regs + 0x01); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n", 34662306a36Sopenharmony_ci bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci reset_one_i2c(bp); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return bp; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cifail: 35362306a36Sopenharmony_ci if (bp->i2c_bussel_reg) 35462306a36Sopenharmony_ci of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1); 35562306a36Sopenharmony_ci if (bp->i2c_control_regs) 35662306a36Sopenharmony_ci of_iounmap(&op->resource[0], bp->i2c_control_regs, 2); 35762306a36Sopenharmony_ci kfree(bp); 35862306a36Sopenharmony_ci return NULL; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciextern int bbc_envctrl_init(struct bbc_i2c_bus *bp); 36262306a36Sopenharmony_ciextern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int bbc_i2c_probe(struct platform_device *op) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct bbc_i2c_bus *bp; 36762306a36Sopenharmony_ci int err, index = 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci bp = attach_one_i2c(op, index); 37062306a36Sopenharmony_ci if (!bp) 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci err = bbc_envctrl_init(bp); 37462306a36Sopenharmony_ci if (err) { 37562306a36Sopenharmony_ci free_irq(op->archdata.irqs[0], bp); 37662306a36Sopenharmony_ci if (bp->i2c_bussel_reg) 37762306a36Sopenharmony_ci of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); 37862306a36Sopenharmony_ci if (bp->i2c_control_regs) 37962306a36Sopenharmony_ci of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); 38062306a36Sopenharmony_ci kfree(bp); 38162306a36Sopenharmony_ci } else { 38262306a36Sopenharmony_ci dev_set_drvdata(&op->dev, bp); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return err; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int bbc_i2c_remove(struct platform_device *op) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci bbc_envctrl_cleanup(bp); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci free_irq(op->archdata.irqs[0], bp); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (bp->i2c_bussel_reg) 39762306a36Sopenharmony_ci of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); 39862306a36Sopenharmony_ci if (bp->i2c_control_regs) 39962306a36Sopenharmony_ci of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci kfree(bp); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic const struct of_device_id bbc_i2c_match[] = { 40762306a36Sopenharmony_ci { 40862306a36Sopenharmony_ci .name = "i2c", 40962306a36Sopenharmony_ci .compatible = "SUNW,bbc-i2c", 41062306a36Sopenharmony_ci }, 41162306a36Sopenharmony_ci {}, 41262306a36Sopenharmony_ci}; 41362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bbc_i2c_match); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic struct platform_driver bbc_i2c_driver = { 41662306a36Sopenharmony_ci .driver = { 41762306a36Sopenharmony_ci .name = "bbc_i2c", 41862306a36Sopenharmony_ci .of_match_table = bbc_i2c_match, 41962306a36Sopenharmony_ci }, 42062306a36Sopenharmony_ci .probe = bbc_i2c_probe, 42162306a36Sopenharmony_ci .remove = bbc_i2c_remove, 42262306a36Sopenharmony_ci}; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cimodule_platform_driver(bbc_i2c_driver); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 427