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