1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * I2C driver for Marvell 88PM860x
4 *
5 * Copyright (C) 2009 Marvell International Ltd.
6 *
7 * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
8 */
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/i2c.h>
12#include <linux/regmap.h>
13#include <linux/mfd/88pm860x.h>
14
15int pm860x_reg_read(struct i2c_client *i2c, int reg)
16{
17	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
18	struct regmap *map = (i2c == chip->client) ? chip->regmap
19				: chip->regmap_companion;
20	unsigned int data;
21	int ret;
22
23	ret = regmap_read(map, reg, &data);
24	if (ret < 0)
25		return ret;
26	else
27		return (int)data;
28}
29EXPORT_SYMBOL(pm860x_reg_read);
30
31int pm860x_reg_write(struct i2c_client *i2c, int reg,
32		     unsigned char data)
33{
34	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
35	struct regmap *map = (i2c == chip->client) ? chip->regmap
36				: chip->regmap_companion;
37	int ret;
38
39	ret = regmap_write(map, reg, data);
40	return ret;
41}
42EXPORT_SYMBOL(pm860x_reg_write);
43
44int pm860x_bulk_read(struct i2c_client *i2c, int reg,
45		     int count, unsigned char *buf)
46{
47	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
48	struct regmap *map = (i2c == chip->client) ? chip->regmap
49				: chip->regmap_companion;
50	int ret;
51
52	ret = regmap_raw_read(map, reg, buf, count);
53	return ret;
54}
55EXPORT_SYMBOL(pm860x_bulk_read);
56
57int pm860x_bulk_write(struct i2c_client *i2c, int reg,
58		      int count, unsigned char *buf)
59{
60	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
61	struct regmap *map = (i2c == chip->client) ? chip->regmap
62				: chip->regmap_companion;
63	int ret;
64
65	ret = regmap_raw_write(map, reg, buf, count);
66	return ret;
67}
68EXPORT_SYMBOL(pm860x_bulk_write);
69
70int pm860x_set_bits(struct i2c_client *i2c, int reg,
71		    unsigned char mask, unsigned char data)
72{
73	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
74	struct regmap *map = (i2c == chip->client) ? chip->regmap
75				: chip->regmap_companion;
76	int ret;
77
78	ret = regmap_update_bits(map, reg, mask, data);
79	return ret;
80}
81EXPORT_SYMBOL(pm860x_set_bits);
82
83static int read_device(struct i2c_client *i2c, int reg,
84		       int bytes, void *dest)
85{
86	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3];
87	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2];
88	struct i2c_adapter *adap = i2c->adapter;
89	struct i2c_msg msg[2] = {
90					{
91						.addr = i2c->addr,
92						.flags = 0,
93						.len = 1,
94						.buf = msgbuf0
95					},
96					{	.addr = i2c->addr,
97						.flags = I2C_M_RD,
98						.len = 0,
99						.buf = msgbuf1
100					},
101				};
102	int num = 1, ret = 0;
103
104	if (dest == NULL)
105		return -EINVAL;
106	msgbuf0[0] = (unsigned char)reg;	/* command */
107	msg[1].len = bytes;
108
109	/* if data needs to read back, num should be 2 */
110	if (bytes > 0)
111		num = 2;
112	ret = adap->algo->master_xfer(adap, msg, num);
113	memcpy(dest, msgbuf1, bytes);
114	if (ret < 0)
115		return ret;
116	return 0;
117}
118
119static int write_device(struct i2c_client *i2c, int reg,
120			int bytes, void *src)
121{
122	unsigned char buf[2];
123	struct i2c_adapter *adap = i2c->adapter;
124	struct i2c_msg msg;
125	int ret;
126
127	buf[0] = (unsigned char)reg;
128	memcpy(&buf[1], src, bytes);
129	msg.addr = i2c->addr;
130	msg.flags = 0;
131	msg.len = bytes + 1;
132	msg.buf = buf;
133
134	ret = adap->algo->master_xfer(adap, &msg, 1);
135	if (ret < 0)
136		return ret;
137	return 0;
138}
139
140int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
141			  unsigned char data)
142{
143	unsigned char zero;
144	int ret;
145
146	i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
147	read_device(i2c, 0xFA, 0, &zero);
148	read_device(i2c, 0xFB, 0, &zero);
149	read_device(i2c, 0xFF, 0, &zero);
150	ret = write_device(i2c, reg, 1, &data);
151	read_device(i2c, 0xFE, 0, &zero);
152	read_device(i2c, 0xFC, 0, &zero);
153	i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
154	return ret;
155}
156EXPORT_SYMBOL(pm860x_page_reg_write);
157
158int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
159			  int count, unsigned char *buf)
160{
161	unsigned char zero = 0;
162	int ret;
163
164	i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
165	read_device(i2c, 0xfa, 0, &zero);
166	read_device(i2c, 0xfb, 0, &zero);
167	read_device(i2c, 0xff, 0, &zero);
168	ret = read_device(i2c, reg, count, buf);
169	read_device(i2c, 0xFE, 0, &zero);
170	read_device(i2c, 0xFC, 0, &zero);
171	i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
172	return ret;
173}
174EXPORT_SYMBOL(pm860x_page_bulk_read);
175