18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Gateworks I2C PLD GPIO expander
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// Based on code and know-how from the OpenWrt driver:
88c2ecf20Sopenharmony_ci// Copyright (C) 2009 Gateworks Corporation
98c2ecf20Sopenharmony_ci// Authors: Chris Lang, Imre Kaloz
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bits.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/**
198c2ecf20Sopenharmony_ci * struct gw_pld - State container for Gateworks PLD
208c2ecf20Sopenharmony_ci * @chip: GPIO chip instance
218c2ecf20Sopenharmony_ci * @client: I2C client
228c2ecf20Sopenharmony_ci * @out: shadow register for the output bute
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistruct gw_pld {
258c2ecf20Sopenharmony_ci	struct gpio_chip chip;
268c2ecf20Sopenharmony_ci	struct i2c_client *client;
278c2ecf20Sopenharmony_ci	u8 out;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * The Gateworks I2C PLD chip only expose one read and one write register.
328c2ecf20Sopenharmony_ci * Writing a "one" bit (to match the reset state) lets that pin be used as an
338c2ecf20Sopenharmony_ci * input. It is an open-drain model.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic int gw_pld_input8(struct gpio_chip *gc, unsigned offset)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct gw_pld *gw = gpiochip_get_data(gc);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	gw->out |= BIT(offset);
408c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(gw->client, gw->out);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int gw_pld_get8(struct gpio_chip *gc, unsigned offset)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct gw_pld *gw = gpiochip_get_data(gc);
468c2ecf20Sopenharmony_ci	s32 val;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte(gw->client);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return (val < 0) ? 0 : !!(val & BIT(offset));
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct gw_pld *gw = gpiochip_get_data(gc);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (value)
588c2ecf20Sopenharmony_ci		gw->out |= BIT(offset);
598c2ecf20Sopenharmony_ci	else
608c2ecf20Sopenharmony_ci		gw->out &= ~BIT(offset);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(gw->client, gw->out);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	gw_pld_output8(gc, offset, value);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int gw_pld_probe(struct i2c_client *client,
718c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
748c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
758c2ecf20Sopenharmony_ci	struct gw_pld *gw;
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL);
798c2ecf20Sopenharmony_ci	if (!gw)
808c2ecf20Sopenharmony_ci		return -ENOMEM;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	gw->chip.base = -1;
838c2ecf20Sopenharmony_ci	gw->chip.can_sleep = true;
848c2ecf20Sopenharmony_ci	gw->chip.parent = dev;
858c2ecf20Sopenharmony_ci	gw->chip.of_node = np;
868c2ecf20Sopenharmony_ci	gw->chip.owner = THIS_MODULE;
878c2ecf20Sopenharmony_ci	gw->chip.label = dev_name(dev);
888c2ecf20Sopenharmony_ci	gw->chip.ngpio = 8;
898c2ecf20Sopenharmony_ci	gw->chip.direction_input = gw_pld_input8;
908c2ecf20Sopenharmony_ci	gw->chip.get = gw_pld_get8;
918c2ecf20Sopenharmony_ci	gw->chip.direction_output = gw_pld_output8;
928c2ecf20Sopenharmony_ci	gw->chip.set = gw_pld_set8;
938c2ecf20Sopenharmony_ci	gw->client = client;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/*
968c2ecf20Sopenharmony_ci	 * The Gateworks I2C PLD chip does not properly send the acknowledge
978c2ecf20Sopenharmony_ci	 * bit at all times, but we can still use the standard i2c_smbus
988c2ecf20Sopenharmony_ci	 * functions by simply ignoring this bit.
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	client->flags |= I2C_M_IGNORE_NAK;
1018c2ecf20Sopenharmony_ci	gw->out = 0xFF;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, gw);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, &gw->chip, gw);
1068c2ecf20Sopenharmony_ci	if (ret)
1078c2ecf20Sopenharmony_ci		return ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	dev_info(dev, "registered Gateworks PLD GPIO device\n");
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic const struct i2c_device_id gw_pld_id[] = {
1158c2ecf20Sopenharmony_ci	{ "gw-pld", },
1168c2ecf20Sopenharmony_ci	{ }
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, gw_pld_id);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic const struct of_device_id gw_pld_dt_ids[] = {
1218c2ecf20Sopenharmony_ci	{ .compatible = "gateworks,pld-gpio", },
1228c2ecf20Sopenharmony_ci	{ },
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gw_pld_dt_ids);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic struct i2c_driver gw_pld_driver = {
1278c2ecf20Sopenharmony_ci	.driver = {
1288c2ecf20Sopenharmony_ci		.name = "gw_pld",
1298c2ecf20Sopenharmony_ci		.of_match_table = gw_pld_dt_ids,
1308c2ecf20Sopenharmony_ci	},
1318c2ecf20Sopenharmony_ci	.probe = gw_pld_probe,
1328c2ecf20Sopenharmony_ci	.id_table = gw_pld_id,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_cimodule_i2c_driver(gw_pld_driver);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
138