diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-sh_mobile.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 191 | 
1 files changed, 86 insertions, 105 deletions
| diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index c03acdf71397..d856bc211715 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -40,21 +40,21 @@  /* BUS:     S     A8     ACK   P(*)                                         */  /* IRQ:       DTE   WAIT                                                    */  /* ICIC:                                                                    */ -/* ICCR: 0x94 0x90                                                          */ +/* ICCR: 0x94       0x90                                                    */  /* ICDR:      A8                                                            */  /*                                                                          */  /* 1 byte transmit                                                          */  /* BUS:     S     A8     ACK   D8(1)   ACK   P(*)                           */  /* IRQ:       DTE   WAIT         WAIT                                       */  /* ICIC:      -DTE                                                          */ -/* ICCR: 0x94       0x90                                                    */ +/* ICCR: 0x94                    0x90                                       */  /* ICDR:      A8    D8(1)                                                   */  /*                                                                          */  /* 2 byte transmit                                                          */  /* BUS:     S     A8     ACK   D8(1)   ACK   D8(2)   ACK   P(*)             */  /* IRQ:       DTE   WAIT         WAIT          WAIT                         */  /* ICIC:      -DTE                                                          */ -/* ICCR: 0x94                    0x90                                       */ +/* ICCR: 0x94                                  0x90                         */  /* ICDR:      A8    D8(1)        D8(2)                                      */  /*                                                                          */  /* 3 bytes or more, +---------+ gets repeated                               */ @@ -113,7 +113,6 @@ enum sh_mobile_i2c_op {  	OP_TX_FIRST,  	OP_TX,  	OP_TX_STOP, -	OP_TX_STOP_DATA,  	OP_TX_TO_RX,  	OP_RX,  	OP_RX_STOP, @@ -145,11 +144,12 @@ struct sh_mobile_i2c_data {  	struct dma_chan *dma_rx;  	struct scatterlist sg;  	enum dma_data_direction dma_direction; +	u8 *dma_buf;  };  struct sh_mobile_dt_config {  	int clks_per_count; -	void (*setup)(struct sh_mobile_i2c_data *pd); +	int (*setup)(struct sh_mobile_i2c_data *pd);  };  #define IIC_FLAG_HAS_ICIC67	(1 << 0) @@ -246,36 +246,10 @@ static u32 sh_mobile_i2c_icch(unsigned long count_khz, u32 tHIGH, u32 tf)  	return (((count_khz * (tHIGH + tf)) + 5000) / 10000);  } -static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd) +static int sh_mobile_i2c_check_timing(struct sh_mobile_i2c_data *pd)  { -	unsigned long i2c_clk_khz; -	u32 tHIGH, tLOW, tf; -	uint16_t max_val; - -	/* Get clock rate after clock is enabled */ -	clk_prepare_enable(pd->clk); -	i2c_clk_khz = clk_get_rate(pd->clk) / 1000; -	clk_disable_unprepare(pd->clk); -	i2c_clk_khz /= pd->clks_per_count; - -	if (pd->bus_speed == STANDARD_MODE) { -		tLOW	= 47;	/* tLOW = 4.7 us */ -		tHIGH	= 40;	/* tHD;STA = tHIGH = 4.0 us */ -		tf	= 3;	/* tf = 0.3 us */ -	} else if (pd->bus_speed == FAST_MODE) { -		tLOW	= 13;	/* tLOW = 1.3 us */ -		tHIGH	= 6;	/* tHD;STA = tHIGH = 0.6 us */ -		tf	= 3;	/* tf = 0.3 us */ -	} else { -		dev_err(pd->dev, "unrecognized bus speed %lu Hz\n", -			pd->bus_speed); -		return -EINVAL; -	} - -	pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf); -	pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf); +	u16 max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff; -	max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff;  	if (pd->iccl > max_val || pd->icch > max_val) {  		dev_err(pd->dev, "timing values out of range: L/H=0x%x/0x%x\n",  			pd->iccl, pd->icch); @@ -298,35 +272,43 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)  	return 0;  } -static void activate_ch(struct sh_mobile_i2c_data *pd) +static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)  { -	/* Wake up device and enable clock */ -	pm_runtime_get_sync(pd->dev); -	clk_prepare_enable(pd->clk); +	unsigned long i2c_clk_khz; +	u32 tHIGH, tLOW, tf; -	/* Enable channel and configure rx ack */ -	iic_set_clr(pd, ICCR, ICCR_ICE, 0); +	i2c_clk_khz = clk_get_rate(pd->clk) / 1000 / pd->clks_per_count; -	/* Mask all interrupts */ -	iic_wr(pd, ICIC, 0); +	if (pd->bus_speed == STANDARD_MODE) { +		tLOW	= 47;	/* tLOW = 4.7 us */ +		tHIGH	= 40;	/* tHD;STA = tHIGH = 4.0 us */ +		tf	= 3;	/* tf = 0.3 us */ +	} else if (pd->bus_speed == FAST_MODE) { +		tLOW	= 13;	/* tLOW = 1.3 us */ +		tHIGH	= 6;	/* tHD;STA = tHIGH = 0.6 us */ +		tf	= 3;	/* tf = 0.3 us */ +	} else { +		dev_err(pd->dev, "unrecognized bus speed %lu Hz\n", +			pd->bus_speed); +		return -EINVAL; +	} -	/* Set the clock */ -	iic_wr(pd, ICCL, pd->iccl & 0xff); -	iic_wr(pd, ICCH, pd->icch & 0xff); +	pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf); +	pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf); + +	return sh_mobile_i2c_check_timing(pd);  } -static void deactivate_ch(struct sh_mobile_i2c_data *pd) +static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd)  { -	/* Clear/disable interrupts */ -	iic_wr(pd, ICSR, 0); -	iic_wr(pd, ICIC, 0); +	unsigned long clks_per_cycle; -	/* Disable channel */ -	iic_set_clr(pd, ICCR, 0, ICCR_ICE); +	/* L = 5, H = 4, L + H = 9 */ +	clks_per_cycle = clk_get_rate(pd->clk) / pd->bus_speed; +	pd->iccl = DIV_ROUND_UP(clks_per_cycle * 5 / 9 - 1, pd->clks_per_count); +	pd->icch = DIV_ROUND_UP(clks_per_cycle * 4 / 9 - 5, pd->clks_per_count); -	/* Disable clock and mark device as idle */ -	clk_disable_unprepare(pd->clk); -	pm_runtime_put_sync(pd->dev); +	return sh_mobile_i2c_check_timing(pd);  }  static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, @@ -350,10 +332,7 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,  	case OP_TX: /* write data */  		iic_wr(pd, ICDR, data);  		break; -	case OP_TX_STOP_DATA: /* write data and issue a stop afterwards */ -		iic_wr(pd, ICDR, data); -		/* fallthrough */ -	case OP_TX_STOP: /* issue a stop */ +	case OP_TX_STOP: /* issue a stop (or rep_start) */  		iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS  					       : ICCR_ICE | ICCR_TRS | ICCR_BBSY);  		break; @@ -387,11 +366,6 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)  	return pd->pos == -1;  } -static bool sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) -{ -	return pd->pos == pd->msg->len - 1; -} -  static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,  				   unsigned char *buf)  { @@ -409,20 +383,12 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)  	unsigned char data;  	if (pd->pos == pd->msg->len) { -		/* Send stop if we haven't yet (DMA case) */ -		if (pd->send_stop && pd->stop_after_dma) -			i2c_op(pd, OP_TX_STOP, 0); +		i2c_op(pd, OP_TX_STOP, 0);  		return 1;  	}  	sh_mobile_i2c_get_data(pd, &data); - -	if (sh_mobile_i2c_is_last_byte(pd)) -		i2c_op(pd, OP_TX_STOP_DATA, data); -	else if (sh_mobile_i2c_is_first_byte(pd)) -		i2c_op(pd, OP_TX_FIRST, data); -	else -		i2c_op(pd, OP_TX, data); +	i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data);  	pd->pos++;  	return 0; @@ -464,8 +430,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)  				break;  			}  			data = i2c_op(pd, OP_RX_STOP_DATA, 0); -		} else +		} else if (real_pos >= 0) {  			data = i2c_op(pd, OP_RX, 0); +		}  		if (real_pos >= 0)  			pd->msg->buf[real_pos] = data; @@ -548,6 +515,8 @@ static void sh_mobile_i2c_dma_callback(void *data)  	pd->pos = pd->msg->len;  	pd->stop_after_dma = true; +	i2c_release_dma_safe_msg_buf(pd->msg, pd->dma_buf); +  	iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);  } @@ -608,7 +577,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)  	if (IS_ERR(chan))  		return; -	dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir); +	dma_addr = dma_map_single(chan->device->dev, pd->dma_buf, pd->msg->len, dir);  	if (dma_mapping_error(chan->device->dev, dma_addr)) {  		dev_dbg(pd->dev, "dma map failed, using PIO\n");  		return; @@ -651,10 +620,10 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,  	if (do_init) {  		/* Initialize channel registers */ -		iic_set_clr(pd, ICCR, 0, ICCR_ICE); +		iic_wr(pd, ICCR, ICCR_SCP);  		/* Enable channel and configure rx ack */ -		iic_set_clr(pd, ICCR, ICCR_ICE, 0); +		iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP);  		/* Set the clock */  		iic_wr(pd, ICCL, pd->iccl & 0xff); @@ -665,7 +634,8 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,  	pd->pos = -1;  	pd->sr = 0; -	if (pd->msg->len > 8) +	pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8); +	if (pd->dma_buf)  		sh_mobile_i2c_xfer_dma(pd);  	/* Enable all interrupts to begin with */ @@ -731,7 +701,8 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,  	int i;  	long timeout; -	activate_ch(pd); +	/* Wake up device and enable clock */ +	pm_runtime_get_sync(pd->dev);  	/* Process all messages */  	for (i = 0; i < num; i++) { @@ -768,11 +739,13 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,  			break;  	} -	deactivate_ch(pd); +	/* Disable channel */ +	iic_wr(pd, ICCR, ICCR_SCP); -	if (!err) -		err = num; -	return err; +	/* Disable clock and mark device as idle */ +	pm_runtime_put_sync(pd->dev); + +	return err ?: num;  }  static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) @@ -789,7 +762,7 @@ static const struct i2c_algorithm sh_mobile_i2c_algorithm = {   * r8a7740 chip has lasting errata on I2C I/O pad reset.   * this is work-around for it.   */ -static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) +static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)  {  	iic_set_clr(pd, ICCR, ICCR_ICE, 0);  	iic_rd(pd, ICCR); /* dummy read */ @@ -810,14 +783,23 @@ static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)  	udelay(10);  	iic_wr(pd, ICCR, ICCR_TRS);  	udelay(10); + +	return sh_mobile_i2c_init(pd);  }  static const struct sh_mobile_dt_config default_dt_config = {  	.clks_per_count = 1, +	.setup = sh_mobile_i2c_init,  };  static const struct sh_mobile_dt_config fast_clock_dt_config = {  	.clks_per_count = 2, +	.setup = sh_mobile_i2c_init, +}; + +static const struct sh_mobile_dt_config v2_freq_calc_dt_config = { +	.clks_per_count = 2, +	.setup = sh_mobile_i2c_v2_init,  };  static const struct sh_mobile_dt_config r8a7740_dt_config = { @@ -828,7 +810,7 @@ static const struct sh_mobile_dt_config r8a7740_dt_config = {  static const struct of_device_id sh_mobile_i2c_dt_ids[] = {  	{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },  	{ .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, -	{ .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config }, +	{ .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config },  	{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },  	{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },  	{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, @@ -910,32 +892,13 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)  		return PTR_ERR(pd->reg);  	ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed); -	pd->bus_speed = ret ? STANDARD_MODE : bus_speed; +	pd->bus_speed = (ret || !bus_speed) ? STANDARD_MODE : bus_speed;  	pd->clks_per_count = 1; -	config = of_device_get_match_data(&dev->dev); -	if (config) { -		pd->clks_per_count = config->clks_per_count; - -		if (config->setup) -			config->setup(pd); -	} - -	/* The IIC blocks on SH-Mobile ARM processors -	 * come with two new bits in ICIC. -	 */ +	/* Newer variants come with two new bits in ICIC */  	if (resource_size(res) > 0x17)  		pd->flags |= IIC_FLAG_HAS_ICIC67; -	ret = sh_mobile_i2c_init(pd); -	if (ret) -		return ret; - -	/* Init DMA */ -	sg_init_table(&pd->sg, 1); -	pd->dma_direction = DMA_NONE; -	pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER); -  	/* Enable Runtime PM for this device.  	 *  	 * Also tell the Runtime PM core to ignore children @@ -948,6 +911,24 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)  	 */  	pm_suspend_ignore_children(&dev->dev, true);  	pm_runtime_enable(&dev->dev); +	pm_runtime_get_sync(&dev->dev); + +	config = of_device_get_match_data(&dev->dev); +	if (config) { +		pd->clks_per_count = config->clks_per_count; +		ret = config->setup(pd); +	} else { +		ret = sh_mobile_i2c_init(pd); +	} + +	pm_runtime_put_sync(&dev->dev); +	if (ret) +		return ret; + +	/* Init DMA */ +	sg_init_table(&pd->sg, 1); +	pd->dma_direction = DMA_NONE; +	pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER);  	/* setup the private data */  	adap = &pd->adap; |