1ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus/* 2ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * ISP1704 USB Charger Detection driver 3ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * 4ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * Copyright (C) 2010 Nokia Corporation 5f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com> 6ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * 7ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * This program is free software; you can redistribute it and/or modify 8ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * it under the terms of the GNU General Public License as published by 9ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * the Free Software Foundation; either version 2 of the License, or 10ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * (at your option) any later version. 11ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * 12ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * This program is distributed in the hope that it will be useful, 13ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * but WITHOUT ANY WARRANTY; without even the implied warranty of 14ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * GNU General Public License for more details. 16ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * 17ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * You should have received a copy of the GNU General Public License 18ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * along with this program; if not, write to the Free Software 19ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus */ 21ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 22ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/kernel.h> 23ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/module.h> 24ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/err.h> 25ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/init.h> 26ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/types.h> 27ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/device.h> 28ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/sysfs.h> 29ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/platform_device.h> 30ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/power_supply.h> 31ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/delay.h> 3234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel#include <linux/of.h> 3334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel#include <linux/of_gpio.h> 34ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 35ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/usb/otg.h> 36ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/usb/ulpi.h> 37ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/usb/ch9.h> 38ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#include <linux/usb/gadget.h> 392785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi#include <linux/power/isp1704_charger.h> 40ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 41ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus/* Vendor specific Power Control register */ 42ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL 0x3d 43ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_SWCTRL (1 << 0) 44ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_DET_COMP (1 << 1) 45ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2) 46ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3) 47ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4) 48ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_VDAT_DET (1 << 5) 49ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6) 50ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define ISP1704_PWR_CTRL_HWDETECT (1 << 7) 51ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 52ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus#define NXP_VENDOR_ID 0x04cc 53ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 54ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic u16 isp170x_id[] = { 55ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 0x1704, 56ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 0x1707, 57ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus}; 58ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 59ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstruct isp1704_charger { 60ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct device *dev; 61ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct power_supply psy; 62fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus struct usb_phy *phy; 63ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct notifier_block nb; 64ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct work_struct work; 65ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 66bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* properties */ 67746d8fb8c6933337c927f40c9ef90dcbddcfd39eAmeya Palande char model[8]; 68ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus unsigned present:1; 69bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus unsigned online:1; 70bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus unsigned current_max; 71ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus}; 72ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 73fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerusstatic inline int isp1704_read(struct isp1704_charger *isp, u32 reg) 74fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus{ 75fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus return usb_phy_io_read(isp->phy, reg); 76fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus} 77fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus 78fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerusstatic inline int isp1704_write(struct isp1704_charger *isp, u32 val, u32 reg) 79fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus{ 80fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus return usb_phy_io_write(isp->phy, val, reg); 81fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus} 82fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus 83ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus/* 842785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi * Disable/enable the power from the isp1704 if a function for it 852785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi * has been provided with platform data. 862785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi */ 872785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemistatic void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) 882785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi{ 892785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi struct isp1704_charger_data *board = isp->dev->platform_data; 902785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi 91c934502db7fda4fac1bb3b5c74f35e9bde8e7547Felipe Contreras if (board && board->set_power) 922785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi board->set_power(on); 9334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel else if (board) 9434a109610e2a78b56be22fa314054c09669075ebSebastian Reichel gpio_set_value(board->enable_gpio, on); 952785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi} 962785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi 972785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi/* 98bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB 99bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * chargers). 100bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * 101bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * REVISIT: The method is defined in Battery Charging Specification and is 102bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * applicable to any ULPI transceiver. Nothing isp170x specific here. 103bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus */ 104bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerusstatic inline int isp1704_charger_type(struct isp1704_charger *isp) 105bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus{ 106bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus u8 reg; 107bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus u8 func_ctrl; 108bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus u8 otg_ctrl; 109bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus int type = POWER_SUPPLY_TYPE_USB_DCP; 110bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 111fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL); 112fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL); 113bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 114bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* disable pulldowns */ 115bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN; 116fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg); 117bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 118bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* full speed */ 119fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL), 120bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus ULPI_FUNC_CTRL_XCVRSEL_MASK); 121fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), 122bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus ULPI_FUNC_CTRL_FULL_SPEED); 123bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 124bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* Enable strong pull-up on DP (1.5K) and reset */ 125bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; 126fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg); 127bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus usleep_range(1000, 2000); 128bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 129fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus reg = isp1704_read(isp, ULPI_DEBUG); 130bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus if ((reg & 3) != 3) 131bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus type = POWER_SUPPLY_TYPE_USB_CDP; 132bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 133bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* recover original state */ 134fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl); 135fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl); 136bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 137bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus return type; 138bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus} 139bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 140bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus/* 141ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger 142ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * is actually a dedicated charger, the following steps need to be taken. 143ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus */ 144ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic inline int isp1704_charger_verify(struct isp1704_charger *isp) 145ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 146ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int ret = 0; 147ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus u8 r; 148ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 149ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Reset the transceiver */ 150fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus r = isp1704_read(isp, ULPI_FUNC_CTRL); 151ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus r |= ULPI_FUNC_CTRL_RESET; 152fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_FUNC_CTRL, r); 153ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus usleep_range(1000, 2000); 154ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 155ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Set normal mode */ 156ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK); 157fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_FUNC_CTRL, r); 158ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 159ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Clear the DP and DM pull-down bits */ 160ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN; 161fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r); 162ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 163ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Enable strong pull-up on DP (1.5K) and reset */ 164ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET; 165fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r); 166ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus usleep_range(1000, 2000); 167ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 168ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Read the line state */ 169fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus if (!isp1704_read(isp, ULPI_DEBUG)) { 170ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Disable strong pull-up on DP (1.5K) */ 171fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL), 172ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ULPI_FUNC_CTRL_TERMSELECT); 173ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return 1; 174ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 175ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 176ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Is it a charger or PS/2 connection */ 177ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 178ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Enable weak pull-up resistor on DP */ 179fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL), 180ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ISP1704_PWR_CTRL_DP_WKPU_EN); 181ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 182ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Disable strong pull-up on DP (1.5K) */ 183fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL), 184ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ULPI_FUNC_CTRL_TERMSELECT); 185ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 186ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Enable weak pull-down resistor on DM */ 187fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL), 188ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ULPI_OTG_CTRL_DM_PULLDOWN); 189ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 190ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* It's a charger if the line states are clear */ 191fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus if (!(isp1704_read(isp, ULPI_DEBUG))) 192ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ret = 1; 193ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 194ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Disable weak pull-up resistor on DP */ 195fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL), 196ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ISP1704_PWR_CTRL_DP_WKPU_EN); 197ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 198ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return ret; 199ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 200ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 201ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic inline int isp1704_charger_detect(struct isp1704_charger *isp) 202ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 203ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus unsigned long timeout; 204bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus u8 pwr_ctrl; 205ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int ret = 0; 206ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 207fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL); 208bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 209ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* set SW control bit in PWR_CTRL register */ 210fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ISP1704_PWR_CTRL, 211ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ISP1704_PWR_CTRL_SWCTRL); 212ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 213ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* enable manual charger detection */ 214fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL), 215bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus ISP1704_PWR_CTRL_SWCTRL 216bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus | ISP1704_PWR_CTRL_DPVSRC_EN); 217ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus usleep_range(1000, 2000); 218ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 219ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus timeout = jiffies + msecs_to_jiffies(300); 220ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus do { 221ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Check if there is a charger */ 222fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus if (isp1704_read(isp, ISP1704_PWR_CTRL) 223ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus & ISP1704_PWR_CTRL_VDAT_DET) { 224ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ret = isp1704_charger_verify(isp); 225ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus break; 226ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 227bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus } while (!time_after(jiffies, timeout) && isp->online); 228bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 229bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* recover original state */ 230fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl); 231ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 232ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return ret; 233ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 234ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 235f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohárstatic inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp) 236f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár{ 237f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp1704_charger_detect(isp) && 238f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP) 239f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár return true; 240f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár else 241f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár return false; 242f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár} 243f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár 244ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic void isp1704_charger_work(struct work_struct *data) 245ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 246ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct isp1704_charger *isp = 247ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus container_of(data, struct isp1704_charger, work); 248bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus static DEFINE_MUTEX(lock); 249ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 250bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus mutex_lock(&lock); 251ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 252f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár switch (isp->phy->last_event) { 253ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus case USB_EVENT_VBUS: 254f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár /* do not call wall charger detection more times */ 255f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (!isp->present) { 256f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->online = true; 257f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->present = 1; 258f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp1704_charger_set_power(isp, 1); 259f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár 260f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár /* detect wall charger */ 261f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp1704_charger_detect_dcp(isp)) { 262f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP; 263f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->current_max = 1800; 264f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár } else { 265f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->psy.type = POWER_SUPPLY_TYPE_USB; 266f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->current_max = 500; 267f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár } 268bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 269f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár /* enable data pullups */ 270f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->phy->otg->gadget) 271f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár usb_gadget_connect(isp->phy->otg->gadget); 272bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus } 273bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 274f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) { 275bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* 276bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * Only 500mA here or high speed chirp 277bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * handshaking may break 278bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus */ 279f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->current_max > 500) 280f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->current_max = 500; 281f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár 282f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->current_max > 100) 283f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP; 284bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus } 285ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus break; 286ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus case USB_EVENT_NONE: 287bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus isp->online = false; 288bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus isp->present = 0; 289bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus isp->current_max = 0; 290bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus isp->psy.type = POWER_SUPPLY_TYPE_USB; 291bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 292bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus /* 293bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * Disable data pullups. We need to prevent the controller from 294bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * enumerating. 295bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * 296bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * FIXME: This is here to allow charger detection with Host/HUB 297bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * chargers. The pullups may be enabled elsewhere, so this can 298bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus * not be the final solution. 299bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus */ 300b1c711d629358576e8896a18e74cd5f4d811d7f7Heikki Krogerus if (isp->phy->otg->gadget) 301b1c711d629358576e8896a18e74cd5f4d811d7f7Heikki Krogerus usb_gadget_disconnect(isp->phy->otg->gadget); 3022785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi 3032785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi isp1704_charger_set_power(isp, 0); 304bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus break; 305ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus default: 306bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus goto out; 307ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 308ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 309bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus power_supply_changed(&isp->psy); 310bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerusout: 311bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus mutex_unlock(&lock); 312bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus} 313bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 314bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerusstatic int isp1704_notifier_call(struct notifier_block *nb, 315f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár unsigned long val, void *v) 316bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus{ 317bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus struct isp1704_charger *isp = 318bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus container_of(nb, struct isp1704_charger, nb); 319bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 320bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus schedule_work(&isp->work); 321bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus 322ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return NOTIFY_OK; 323ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 324ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 325ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic int isp1704_charger_get_property(struct power_supply *psy, 326ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus enum power_supply_property psp, 327ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus union power_supply_propval *val) 328ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 329ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct isp1704_charger *isp = 330ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus container_of(psy, struct isp1704_charger, psy); 331ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 332ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus switch (psp) { 333ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus case POWER_SUPPLY_PROP_PRESENT: 334ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus val->intval = isp->present; 335ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus break; 336bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus case POWER_SUPPLY_PROP_ONLINE: 337bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus val->intval = isp->online; 338bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus break; 339bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus case POWER_SUPPLY_PROP_CURRENT_MAX: 340bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus val->intval = isp->current_max; 341bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus break; 342ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus case POWER_SUPPLY_PROP_MODEL_NAME: 343ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus val->strval = isp->model; 344ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus break; 345ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus case POWER_SUPPLY_PROP_MANUFACTURER: 346ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus val->strval = "NXP"; 347ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus break; 348ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus default: 349ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return -EINVAL; 350ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 351ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return 0; 352ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 353ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 354ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic enum power_supply_property power_props[] = { 355ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus POWER_SUPPLY_PROP_PRESENT, 356bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus POWER_SUPPLY_PROP_ONLINE, 357bac43b20501058ab0728246acce3bb85f2e72648Heikki Krogerus POWER_SUPPLY_PROP_CURRENT_MAX, 358ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus POWER_SUPPLY_PROP_MODEL_NAME, 359ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus POWER_SUPPLY_PROP_MANUFACTURER, 360ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus}; 361ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 362ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic inline int isp1704_test_ulpi(struct isp1704_charger *isp) 363ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 364ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int vendor; 365ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int product; 366ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int i; 367ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int ret = -ENODEV; 368ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 369ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Test ULPI interface */ 370fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa); 371ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret < 0) 372ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return ret; 373ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 374fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus ret = isp1704_read(isp, ULPI_SCRATCH); 375ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret < 0) 376ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return ret; 377ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 378ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret != 0xaa) 379ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return -ENODEV; 380ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 381ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* Verify the product and vendor id matches */ 382fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW); 383fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8; 384ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (vendor != NXP_VENDOR_ID) 385ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return -ENODEV; 386ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 387fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW); 388fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8; 389ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 390ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) { 391ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (product == isp170x_id[i]) { 392ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus sprintf(isp->model, "isp%x", product); 393ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return product; 394ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 395ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus } 396ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 397ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus dev_err(isp->dev, "product id %x not matching known ids", product); 398ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 399ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return -ENODEV; 400ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 401ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 402c8afa6406e60aec6ff90033e5ffe41a206609296Bill Pembertonstatic int isp1704_charger_probe(struct platform_device *pdev) 403ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 404ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct isp1704_charger *isp; 405ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus int ret = -ENODEV; 406ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 40734a109610e2a78b56be22fa314054c09669075ebSebastian Reichel struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev); 40834a109610e2a78b56be22fa314054c09669075ebSebastian Reichel struct device_node *np = pdev->dev.of_node; 40934a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 41034a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (np) { 41134a109610e2a78b56be22fa314054c09669075ebSebastian Reichel int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0); 41234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 41334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (gpio < 0) 41434a109610e2a78b56be22fa314054c09669075ebSebastian Reichel return gpio; 41534a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 41634a109610e2a78b56be22fa314054c09669075ebSebastian Reichel pdata = devm_kzalloc(&pdev->dev, 41734a109610e2a78b56be22fa314054c09669075ebSebastian Reichel sizeof(struct isp1704_charger_data), GFP_KERNEL); 41834a109610e2a78b56be22fa314054c09669075ebSebastian Reichel pdata->enable_gpio = gpio; 41934a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 42034a109610e2a78b56be22fa314054c09669075ebSebastian Reichel dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio); 42134a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 42234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, 42334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel GPIOF_OUT_INIT_HIGH, "isp1704_reset"); 42434a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (ret) 42534a109610e2a78b56be22fa314054c09669075ebSebastian Reichel goto fail0; 42634a109610e2a78b56be22fa314054c09669075ebSebastian Reichel } 42734a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 42834a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (!pdata) { 42934a109610e2a78b56be22fa314054c09669075ebSebastian Reichel dev_err(&pdev->dev, "missing platform data!\n"); 43034a109610e2a78b56be22fa314054c09669075ebSebastian Reichel return -ENODEV; 43134a109610e2a78b56be22fa314054c09669075ebSebastian Reichel } 43234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 43334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 4342a2ce52a4006db6c7831f1b21f1b0cc892516e85Jingoo Han isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); 435ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (!isp) 436ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return -ENOMEM; 437ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 43834a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (np) 43934a109610e2a78b56be22fa314054c09669075ebSebastian Reichel isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); 44034a109610e2a78b56be22fa314054c09669075ebSebastian Reichel else 44134a109610e2a78b56be22fa314054c09669075ebSebastian Reichel isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 44234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 44334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel if (IS_ERR(isp->phy)) { 44434a109610e2a78b56be22fa314054c09669075ebSebastian Reichel ret = PTR_ERR(isp->phy); 44534a109610e2a78b56be22fa314054c09669075ebSebastian Reichel goto fail0; 44634a109610e2a78b56be22fa314054c09669075ebSebastian Reichel } 447ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 448a4607d9f5cbae1aad7f86e8204b57d66c0e14e36Heikki Krogerus isp->dev = &pdev->dev; 449a4607d9f5cbae1aad7f86e8204b57d66c0e14e36Heikki Krogerus platform_set_drvdata(pdev, isp); 450a4607d9f5cbae1aad7f86e8204b57d66c0e14e36Heikki Krogerus 4512785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi isp1704_charger_set_power(isp, 1); 4522785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi 453ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ret = isp1704_test_ulpi(isp); 454ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret < 0) 455ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus goto fail1; 456ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 457ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->psy.name = "isp1704"; 458ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->psy.type = POWER_SUPPLY_TYPE_USB; 459ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->psy.properties = power_props; 460ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->psy.num_properties = ARRAY_SIZE(power_props); 461ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->psy.get_property = isp1704_charger_get_property; 462ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 463ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus ret = power_supply_register(isp->dev, &isp->psy); 464ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret) 465ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus goto fail1; 466ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 467ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus /* 468fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus * REVISIT: using work in order to allow the usb notifications to be 469ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus * made atomically in the future. 470ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus */ 471ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus INIT_WORK(&isp->work, isp1704_charger_work); 472ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 473ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus isp->nb.notifier_call = isp1704_notifier_call; 474ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 475fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus ret = usb_register_notifier(isp->phy, &isp->nb); 476ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus if (ret) 477ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus goto fail2; 478ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 479ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus dev_info(isp->dev, "registered with product id %s\n", isp->model); 480ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 481e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus /* 482e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus * Taking over the D+ pullup. 483e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus * 484e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus * FIXME: The device will be disconnected if it was already 485e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus * enumerated. The charger driver should be always loaded before any 486e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus * gadget is loaded. 487e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus */ 488b1c711d629358576e8896a18e74cd5f4d811d7f7Heikki Krogerus if (isp->phy->otg->gadget) 489b1c711d629358576e8896a18e74cd5f4d811d7f7Heikki Krogerus usb_gadget_disconnect(isp->phy->otg->gadget); 490e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus 491f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->phy->last_event == USB_EVENT_NONE) 492f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár isp1704_charger_set_power(isp, 0); 493f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár 494e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus /* Detect charger if VBUS is valid (the cable was already plugged). */ 495f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár if (isp->phy->last_event == USB_EVENT_VBUS && 496f07c11ea32eebf81b7c1e59da2d119bf023882f2Pali Rohár !isp->phy->otg->default_a) 497e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus schedule_work(&isp->work); 498e1a85694e08d03efae9e08fdb292dfd4870837bcHeikki Krogerus 499ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return 0; 500ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusfail2: 501ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus power_supply_unregister(&isp->psy); 502ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusfail1: 50381a08382aeb802ad642ab7f2a7786baa7bafaaa3Dan Carpenter isp1704_charger_set_power(isp, 0); 504ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusfail0: 505ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); 506ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 507ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return ret; 508ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 509ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 510415ec69fb1861fc377c65cb30ddc76999891b8e1Bill Pembertonstatic int isp1704_charger_remove(struct platform_device *pdev) 511ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus{ 512ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus struct isp1704_charger *isp = platform_get_drvdata(pdev); 513ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 514fcc8ebc99034bae4020a3cec030553d469e265dbHeikki Krogerus usb_unregister_notifier(isp->phy, &isp->nb); 515ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus power_supply_unregister(&isp->psy); 5162785cefc98051646bd1d36a627822a3f43736697Kalle Jokiniemi isp1704_charger_set_power(isp, 0); 517ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 518ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus return 0; 519ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus} 520ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 52134a109610e2a78b56be22fa314054c09669075ebSebastian Reichel#ifdef CONFIG_OF 52234a109610e2a78b56be22fa314054c09669075ebSebastian Reichelstatic const struct of_device_id omap_isp1704_of_match[] = { 52334a109610e2a78b56be22fa314054c09669075ebSebastian Reichel { .compatible = "nxp,isp1704", }, 52434a109610e2a78b56be22fa314054c09669075ebSebastian Reichel {}, 52534a109610e2a78b56be22fa314054c09669075ebSebastian Reichel}; 52634a109610e2a78b56be22fa314054c09669075ebSebastian ReichelMODULE_DEVICE_TABLE(of, omap_isp1704_of_match); 52734a109610e2a78b56be22fa314054c09669075ebSebastian Reichel#endif 52834a109610e2a78b56be22fa314054c09669075ebSebastian Reichel 529ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerusstatic struct platform_driver isp1704_charger_driver = { 530ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus .driver = { 531ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus .name = "isp1704_charger", 53234a109610e2a78b56be22fa314054c09669075ebSebastian Reichel .of_match_table = of_match_ptr(omap_isp1704_of_match), 533ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus }, 534ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus .probe = isp1704_charger_probe, 53528ea73f4c67cb3dd8c972b21d9fdf84ea78d6daaBill Pemberton .remove = isp1704_charger_remove, 536ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus}; 537ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 538300bac7fb85a20b2704dc3645419057992f78565Axel Linmodule_platform_driver(isp1704_charger_driver); 539ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki Krogerus 540ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki KrogerusMODULE_ALIAS("platform:isp1704_charger"); 541ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki KrogerusMODULE_AUTHOR("Nokia Corporation"); 542ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki KrogerusMODULE_DESCRIPTION("ISP170x USB Charger driver"); 543ec46475f3e3163dd80bfee086fa71b36455ecc0bHeikki KrogerusMODULE_LICENSE("GPL"); 544