162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NXP ISP1301 USB transceiver driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Roland Stigge
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Roland Stigge <stigge@antcom.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/i2c.h>
1362306a36Sopenharmony_ci#include <linux/usb/phy.h>
1462306a36Sopenharmony_ci#include <linux/usb/isp1301.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define DRV_NAME		"isp1301"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct isp1301 {
1962306a36Sopenharmony_ci	struct usb_phy		phy;
2062306a36Sopenharmony_ci	struct mutex		mutex;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	struct i2c_client	*client;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define phy_to_isp(p)		(container_of((p), struct isp1301, phy))
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct i2c_device_id isp1301_id[] = {
2862306a36Sopenharmony_ci	{ "isp1301", 0 },
2962306a36Sopenharmony_ci	{ }
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isp1301_id);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct of_device_id isp1301_of_match[] = {
3462306a36Sopenharmony_ci	{.compatible = "nxp,isp1301" },
3562306a36Sopenharmony_ci	{ },
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, isp1301_of_match);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic struct i2c_client *isp1301_i2c_client;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int __isp1301_write(struct isp1301 *isp, u8 reg, u8 value, u8 clear)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(isp->client, reg | clear, value);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int isp1301_write(struct isp1301 *isp, u8 reg, u8 value)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return __isp1301_write(isp, reg, value, 0);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int isp1301_clear(struct isp1301 *isp, u8 reg, u8 value)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return __isp1301_write(isp, reg, value, ISP1301_I2C_REG_CLEAR_ADDR);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int isp1301_phy_init(struct usb_phy *phy)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct isp1301 *isp = phy_to_isp(phy);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Disable transparent UART mode first */
6162306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_UART_EN);
6262306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, ~MC1_SPEED_REG);
6362306a36Sopenharmony_ci	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_SPEED_REG);
6462306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_2, ~0);
6562306a36Sopenharmony_ci	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_PSW_EN
6662306a36Sopenharmony_ci				| MC2_SPD_SUSP_CTRL));
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, ~0);
6962306a36Sopenharmony_ci	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_DAT_SE0);
7062306a36Sopenharmony_ci	isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLDOWN
7162306a36Sopenharmony_ci				| OTG1_DP_PULLDOWN));
7262306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLUP
7362306a36Sopenharmony_ci				| OTG1_DP_PULLUP));
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* mask all interrupts */
7662306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_LATCH, ~0);
7762306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_FALLING, ~0);
7862306a36Sopenharmony_ci	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_RISING, ~0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int isp1301_phy_set_vbus(struct usb_phy *phy, int on)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct isp1301 *isp = phy_to_isp(phy);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (on)
8862306a36Sopenharmony_ci		isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
8962306a36Sopenharmony_ci	else
9062306a36Sopenharmony_ci		isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int isp1301_probe(struct i2c_client *client)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct isp1301 *isp;
9862306a36Sopenharmony_ci	struct usb_phy *phy;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	isp = devm_kzalloc(&client->dev, sizeof(*isp), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (!isp)
10262306a36Sopenharmony_ci		return -ENOMEM;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	isp->client = client;
10562306a36Sopenharmony_ci	mutex_init(&isp->mutex);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	phy = &isp->phy;
10862306a36Sopenharmony_ci	phy->dev = &client->dev;
10962306a36Sopenharmony_ci	phy->label = DRV_NAME;
11062306a36Sopenharmony_ci	phy->init = isp1301_phy_init;
11162306a36Sopenharmony_ci	phy->set_vbus = isp1301_phy_set_vbus;
11262306a36Sopenharmony_ci	phy->type = USB_PHY_TYPE_USB2;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	i2c_set_clientdata(client, isp);
11562306a36Sopenharmony_ci	usb_add_phy_dev(phy);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	isp1301_i2c_client = client;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void isp1301_remove(struct i2c_client *client)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct isp1301 *isp = i2c_get_clientdata(client);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	usb_remove_phy(&isp->phy);
12762306a36Sopenharmony_ci	isp1301_i2c_client = NULL;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic struct i2c_driver isp1301_driver = {
13162306a36Sopenharmony_ci	.driver = {
13262306a36Sopenharmony_ci		.name = DRV_NAME,
13362306a36Sopenharmony_ci		.of_match_table = isp1301_of_match,
13462306a36Sopenharmony_ci	},
13562306a36Sopenharmony_ci	.probe = isp1301_probe,
13662306a36Sopenharmony_ci	.remove = isp1301_remove,
13762306a36Sopenharmony_ci	.id_table = isp1301_id,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cimodule_i2c_driver(isp1301_driver);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistruct i2c_client *isp1301_get_client(struct device_node *node)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct i2c_client *client;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* reference of ISP1301 I2C node via DT */
14762306a36Sopenharmony_ci	client = of_find_i2c_device_by_node(node);
14862306a36Sopenharmony_ci	if (client)
14962306a36Sopenharmony_ci		return client;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* non-DT: only one ISP1301 chip supported */
15262306a36Sopenharmony_ci	return isp1301_i2c_client;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isp1301_get_client);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
15762306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP ISP1301 USB transceiver driver");
15862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
159